solución ABC de kmp

Un período poj 1961

Para cada prefijo de una cadena dada S con N caracteres (cada carácter tiene un código ASCII entre 97 y 126, inclusive), queremos saber si el prefijo es una cadena periódica. Es decir, para cada i (2 <= i <= N) queremos saber el mayor K> 1 (si lo hay) tal que el prefijo de S con longitud i pueda escribirse como AK, es decir A K concatenado veces, para alguna cadena A. Por supuesto, también queremos saber el período K.
Entrada
La entrada consta de varios casos de prueba. Cada caso de prueba consta de dos líneas. El primero contiene N (2 <= N <= 1 000 000) - el tamaño de la cadena S. La segunda línea contiene la cadena S. El archivo de entrada termina con una línea, que tiene el
número cero.
Producción
Para cada caso de prueba, envíe “Test case #” y el número de caso de prueba consecutivo en una sola línea; luego, para cada prefijo con longitud i que tenga un período K> 1, genere el tamaño del prefijo i y el período K separados por un solo espacio; los tamaños de prefijo deben estar en orden creciente. Imprima una línea en blanco después de cada caso de prueba.
Entrada de muestra
3
aaa
12
aabaabaabaab
0
Salida de muestra
Caso de prueba n. ° 1
2 2
3 3

Caso de prueba n. ° 2
2 2
6 2
9 3
12 4

Solución: el
significado de la pregunta es encontrar el número de apariciones de la sección de bucle en los primeros i caracteres (requiere que el número de secciones de bucle sea mayor que 1), como aabaabaabaab, donde a aparece dos veces en los dos primeros caracteres, y la sección de bucle es un. En los primeros 6 caracteres, la sección de bucle aparece dos veces, la sección de bucle es aab, entre los primeros 9 caracteres, la sección de bucle aparece 3 veces y la sección de bucle es aab; en 12 caracteres, la sección de bucle aparece 4 veces, La sección de bucle es aab. Examine principalmente el uso de la siguiente matriz, i-next [i] es la longitud de la sección del bucle. El
código es el siguiente:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10000007
#define debug() puts("what the fuck!!!")
#define ll long long
#define speed {
    
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
int gcd(int x, int y) {
    
    
	return y ? gcd(y, x % y) : x;
}
string s, p;
int Next[maxn];
int ans;

void getfail(string p) {
    
    
	Next[0] = -1;
	int k = -1;
	int j = 0;
	int plen = p.length();
	while (j < plen) {
    
    
		if (k == -1 || p[j] == p[k]) {
    
    
			j++;
			k++;
			//	if (p[j] != p[k])//kmp查找情况下next优化部分
			Next[j] = k;
			//	else
			//		Next[j] = Next[k];
		}
		else
			k = Next[k];
	}
}
int kmp(string s, string p) {
    
    
	int i = 0, j = 0;
	int plen = p.length();
	int slen = s.length();
	while (i < slen) {
    
    
		if (j == -1 || s[i] == p[j]) {
    
    
			i++;
			j++;
		}
		else
			j = Next[j];
		if (j == plen)
			return i = j;
	}
	return -1;
}
int main() {
    
    
	speed;
	string s, p;
	int n;
	int step = 1;
	while (cin >> n) {
    
    
		if (n == 0)break;
		cin >> p;
		getfail(p);
		cout << "Test case #" << step << endl;
		step++;
		for (int i = 0; i <= n; ++i) {
    
    //因为Next数组是前后缀最大公共元素长度整体右移一个单位的结果,所以要遍历到n
			//Next>0即存在前后缀公共字符,i-Next[i]为该节点前的循环节长度,i%(i-Next[i])==0即前后缀公共子串正好截成相同的几段
			if (Next[i] > 0 && i % (i - Next[i]) == 0)cout << i << " " << i / (i - Next[i]) << endl;
		}
		cout << endl;
	}
	return 0;
}

B HDU 3336 Cuente la cuerda

Es bien sabido que AekdyCoin es bueno tanto en problemas de cuerdas como en problemas de teoría de números. Cuando se le da una cadena s, podemos escribir todos los prefijos no vacíos de esta cadena. Por ejemplo:
s: “abab”
Los prefijos son: “a”, “ab”, “aba”, “abab”
Para cada prefijo, podemos contar las veces que coincide en s. Así que podemos ver que el prefijo "a" coincide dos veces, "ab" coincide también dos veces, "aba" coincide una vez y "abab" coincide una vez. Ahora se le pide que calcule la suma de los tiempos de coincidencia para todos los prefijos. Para “abab”, es 2 + 2 + 1 + 1 = 6.
La respuesta puede ser muy grande, por lo tanto, envíe la respuesta mod 10007.
Entrada
La primera línea es un entero T, que indica el número de casos de prueba.
Para cada caso, la primera línea es un número entero n (1 <= n <= 200000), que es la longitud de la cadena s. Sigue una línea dando la cadena s. Los caracteres de las cadenas son todos letras minúsculas.
Salida
Para cada caso, envíe solo un número: la suma de los tiempos de coincidencia para todos los prefijos de s mod 10007.
Entrada de muestra
1
4
abab
Salida de muestra
6

Solución del problema El
significado del problema es darle una cadena y encontrar la suma del número de veces que todos los prefijos de la cadena coinciden con la cadena en sí. Por ejemplo, abab tiene el prefijo a, ab, aba, abab. Luego use estos 4 prefijos para hacer coincidir abab en sí. Hay 2, 2, 1 y 1 puntos coincidentes, por lo que el total es 2 + 2 + 1 + 1 = 6. Mi idea inicial era definir ans para contar los pasos, y la siguiente matriz registra la coincidencia del prefijo y el sufijo. Si hay una coincidencia, significa que hay una coincidencia, luego ans ++, y luego el prefijo de cadena se puede hacer coincidir una vez a su vez, finalmente ans más n es Para la respuesta. Aunque se puede aprobar (tal vez los datos de la prueba de fondo sean demasiado basura), no es una solución correcta. Por ejemplo, en el ejemplo de aaaa, la salida de solución positiva es 10, pero la respuesta obtenida por el método anterior es 7. No pensé mucho cuando lo estaba haciendo yo mismo, y no me importó después de que lo entregué. No esperaba que este error estuviera allí.
La solución positiva debe hacerse con la matriz dp + next. Cuando i no coincide, volverá a la posición de next [i], por lo que la cadena actual contiene el número de prefijo contenido en next [i] + su propia corriente, para que pueda obtener la ecuación de transición de estado: dp [i] = dp [next [i]] +1 El
código es el siguiente:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10000007
#define debug() puts("what the fuck!!!")
#define ll long long
#define speed {
    
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 2e5 + 10;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
int gcd(int x, int y) {
    
    
	return y ? gcd(y, x % y) : x;
}
string s, p;
int Next[maxn];
int ans;
int dp[maxn];
char str[maxn];
void getFail(char* str) {
    
    
	Next[0] = -1;
	int j = 0;
	int k = -1;
	int plen = strlen(str);
	while (j < plen) {
    
    
		if (k == -1 || str[j] == str[k]) {
    
    
			k++;
			j++;
			Next[j] = k;
		}
		else
			k = Next[k];
	}
}
int main() {
    
    
	int t;
	cin >> t;
	while (t--) {
    
    
		int n;
		scanf("%d", &n);
		scanf("%s", &str);
		getFail(str);
		int ans = 0;
		for (int i = 1; i <= n; i++)//当前串包含了next[i]所包含的前缀数+其当前本身
			dp[i] = (dp[Next[i]] + 1) % 10007, ans = (dp[i] + ans) % 10007;
		printf("%d\n", ans);
	}
	return 0;
}

C POJ - Rejilla de ordeño 2185

Cada mañana, cuando se ordeñan, las vacas de Farmer John forman una cuadrícula rectangular que tiene R (1 <= R <= 10,000) filas por C (1 <= C <= 75) columnas. Como todos sabemos, el granjero John es un experto en el comportamiento de las vacas y actualmente está escribiendo un libro sobre el comportamiento de alimentación de las vacas. Se da cuenta de que si cada vaca está etiquetada con una letra mayúscula que indica su raza, el patrón bidimensional formado por sus vacas durante el ordeño a veces parece estar hecho de patrones rectangulares repetidos más pequeños.

Ayude a FJ a encontrar la unidad rectangular de área más pequeña que se pueda colocar repetidamente en mosaico para formar toda la rejilla de ordeño. Tenga en cuenta que las dimensiones de la unidad rectangular pequeña no necesariamente tienen que dividir uniformemente las dimensiones de toda la rejilla de ordeño, como se indica en la entrada de muestra a continuación.

Aporte

  • Línea 1: dos enteros separados por espacios: R y C

  • Líneas 2… R + 1: La cuadrícula que forman las vacas, con una letra mayúscula que indica la raza de cada vaca. Cada una de las líneas de entrada R tiene caracteres C sin espacios ni ningún otro carácter intermedio.
    Producción

  • Línea 1: El área de la unidad más pequeña a partir de la cual se forma la rejilla
    Entrada de muestra
    2 5
    ABABA
    ABABA
    Salida de muestra
    2
    Sugerencia
    La rejilla de ordeño completa se puede construir a partir de repeticiones del patrón 'AB'.

Solución: La
intención del problema es encontrar el área de la matriz de caracteres más pequeña. Esta matriz se puede hacer un bucle en esta matriz grande, pero no todos los bucles son iguales. La misma parte también se considera un bucle, como la muestra.
La idea específica es tratarlo como un KMP bidimensional, tomar cada cadena como un carácter para encontrar la siguiente matriz y luego encontrar la longitud mínima de la cadena de bucle, a saber: len-next [len].
Debido a que se requieren la longitud y el ancho de esta matriz circulante, la longitud es cada fila como una cadena, y luego se calcula la longitud mínima de la cadena cíclica; el ancho es cada columna como una cadena, y la longitud cíclica también se calcula, y el la multiplicación es la respuesta.

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<map>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<set>
#include<cctype>
#include<string>
#include<stdexcept>
#include<fstream>
#define mem(a,b) memset(a,b,sizeof(a))
#define mod 10000007
#define debug() puts("what the fuck!")
#define dedebug() puts("what the fuck!!!")
#define ll long long
#define ull unsigned long long
#define speed {
    
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); };
using namespace std;
const double PI = acos(-1.0);
const int maxn = 1e4 + 50;
const int INF = 0x3f3f3f3f;
const double esp_0 = 1e-6;
int gcd(int x, int y) {
    
    
	return y ? gcd(y, x % y) : x;
}
int Next[maxn];
char s[maxn][maxn], str[80][maxn], ss[maxn];
void getnexts(int plen, char p[][maxn]) {
    
    
	Next[0] = -1;
	int k = -1;
	int j = 0;
	while (j < plen) {
    
    
		if (k == -1 || strcmp(p[k], p[j]) == 0) {
    
    //将字符串当作字符来处理
			k++;
			j++;
			Next[j] = k;
		}
		else
			k = Next[k];
	}
}
int main() {
    
    
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++)
		scanf("%s", s[i]);
	for (int i = 0; i < m; i++)
		for (int j = 0; j < n; j++)
			str[i][j] = s[j][i];
	mem(Next, 0);
	getnexts(n, s);
	int ans1 = n - Next[n];//求出循环节长度
	//for (int i = 0; i <= n; ++i)cout << Next[i] << " ";
	//cout << endl;
	mem(Next, 0);
	getnexts(m, str);
	int ans2 = m - Next[m];//求出循环节长度
	//for (int i = 0; i <= m; ++i)cout << Next[i] << " ";
	//cout << endl;
	printf("%d", ans1 * ans2);
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/qq_40924271/article/details/107599095
Recomendado
Clasificación