Experiencia secundaria de aprendizaje de hash de cadenas

Si el hash se escribe a mano, la clave principal solo puede ser de tipo int. Si la clave principal es un tipo de cadena y el valor es una asignación de tipo int, el hash no se puede escribir a mano y se debe utilizar unordered_map.

Vea el enlace por primera vez: tabla hash, aprendizaje preliminar de hash de cadenas

Hash de cadena

Convierta una cadena en un número entero y asegúrese de que el valor hash sea diferente si la cadena es diferente. Similar a la idea de representación de números binarios de enteros.

Por lo tanto, una cadena de caracteres se considera un número base R. Para una cadena de caracteres: S = s1s2 ... sn , si son todas letras minúsculas, puede convertir cada carácter en ellas idx(si)=si-'a'+1. Si hay letras mayúsculas, letras minúsculas, y números, es inconveniente El procesamiento puede ser representado directamente por el código ASCII del carácter.

Para la reconversión de caracteres, se debe tener en cuenta que no puede comenzar desde 0, por lo que +1.
Suponiendo que el código de a es 0, entonces los valores hash de "aa", "aaa" y "aaaa" son todos 0, y el conflicto es serio.

Función hash

Convierta una cadena (número base R) en un número decimal mediante la función hash :, Hash[i] = ( Hash[i-1]*R + idx(si) )%modi de 1 an, a través deRecursividadEn el medio, podemos encontrar el valor hash de las subcadenas de prefijo "s1", "s1s2", "s1s2s3" ... hasta "s1s2 ... sn", es decir, el Hash [n] final, el hash de nuestro Valor de la cadena S, esExpansión为 :Hash [n] = (s1 × R n-1 + s2 × R n-2 + …… + s n-1 × R + s n )% mod

Disposiciones: Para unificar la fórmula de recurrencia,Hash[0]=0

Generalmente,
R es 131 o 13331;
mod es 2 64 , y unsigned long long se usa a menudo para desbordar de forma natural, lo que equivale a automáticamente módulo 2 64 . Porque ULL también es de 64 bits

Cabe señalar que esta conversión de método de hash único no garantiza un 100% de ausencia de conflicto, es decir, puede haber el mismo valor de hash (número decimal) correspondiente a dos cadenas, pero el factor de inseguridad es muy pequeño y se puede ignorar. si realmente lo encontró, considere el método de doble hash .

Subcadena de prefijo

Cuando encontramos el valor hash de una cadena, generalmente usamos el método recursivo en lugar de su expansión, por lo que se debe encontrar la subcadena de prefijo de esta cadena , y se puede usar una matriz para registrar Siguiente, con su subcadena de prefijo, podemos calcular el valor hash de cualquier subcadena de esta cadena con solo complejidad O (1): a través de la idea de suma de prefijo .

Conclusión: Encuentre el valor hash correspondiente a la cadena del subíndice l al subíndice r en la cadena principal : h [r] -h [l-1] × R r-l + 1 ULL h[N];

Cadena: S = s1s2 ... sn , asumiendo l = 4, r = 6, entonces se requiere el valor hash de s4s5s6, es decir, s4 × R 2 + s5 × R + s6.
Sabemos que h [6] = s1 × R 5 + s2 × R 4 + s3 × R 3 + s4 × R 2 + s5 × R + s6
 h [4-1] = h [3] = s1 × R 2 + s2 × R + s3
  R 3 × h [3] = s1 × R 5 + s2 × R 4 + s3 × R 3

Sin embargo, se observa que h [6] y h [3] no se pueden restar directamente debido al desajuste de potencias, por lo que primero debemos multiplicar h [3] por un número para igualar las tres primeras potencias de h [6] a obtenga los últimos tres dígitos. Este número es R 6-4 + 1 , es decir, el número de dígitos entre r y l-1, que se pueden restar directamente.

¿Cómo encontrar R r-l + 1 ?
Cuando le pedimos a hash [n] que encuentre recursivamente la subcadena de su prefijo, también podemos encontrar R n , que también es un proceso recursivo :, R^n = R^n-1 × Ruse una matriz para mantener, R [i] es R i

Finalmente, para almacenar la matriz de cadenas, está bien almacenar desde el subíndice 0 o el subíndice 1, pero para ser consistente con el hash de matriz recursiva y R, su fórmula de recursividad involucra i-1, por lo que el subíndice es mejor empezar con 1.

"Hash [0] no representa ninguna subcadena, hash [1] representa una subcadena de prefijo con solo 1 carácter y hash [k] representa una subcadena de prefijo con k caracteres "

Descripción del tema 1

Dada una cadena de longitud n, y luego dadas m consultas, cada consulta contiene cuatro enteros l1, r1, l2, r2, juzgue los dos intervalos [l1, r1] y [l2, r2] Si las subcadenas de cadena contenidas son exactamente las mismo.

En la cadena solo se incluyen letras y números en inglés en mayúsculas y minúsculas.

Formato de entrada La
primera línea contiene los números enteros nym, que representan la longitud de la cadena y el número de consultas.

La segunda línea contiene una cadena de longitud n, que contiene solo letras y números en inglés en mayúsculas y minúsculas.

Las siguientes m líneas, cada línea contiene cuatro números enteros l1, r1, l2, r2, que representan los dos intervalos involucrados en una consulta.

Tenga en cuenta que la posición de la cadena se numera desde 1. Esto es requerido por el tema

Formato de
salida Genera un resultado para cada consulta. Si las dos subcadenas de cadena son exactamente iguales, genera "Sí", de lo contrario genera "No".

Cada resultado ocupa una línea.

Rango de datos
1≤n, m≤10 5

Muestra de entrada:

8 3
padre
1 3 5 7
1 3 6 8
1 2 1 2

Salida de muestra:

Si
no
si

Implementación de algoritmos

#include <iostream>
#define read(x,y) scanf("%d%d",&x,&y)

using namespace std;

typedef unsigned long long ULL;
const int N=1e5+10,R=131;
char str[N];
ULL hs[N]; 维护每个前缀子串的哈希值,"hs[0]代表没有子串,hs[1]代表只有1个字符的前缀子串,hs[k]表示有k个字符的前缀子串"
ULL r[N]={
    
    1}; //维护R^i,r^0=1,其它初始为0,也定义为ULL类型,以防万一

ULL getSub(int l,int h) // low high
{
    
    
    return hs[h]-hs[l-1]*r[h-l+1];    
}

int main()
{
    
    
    int n,num;
    read(n,num);
    scanf("%s",str+1);//从下标1开始存储
    
    //预处理
    for (int i=1;i<=n;i++) {
    
    
        hs[i]=hs[i-1]*R+str[i];
        r[i]=r[i-1]*R;  "前提,r[0]=1"
    }
    
    int l1,r1,l2,r2;
    while (num--) {
    
    
        read(l1,r1),read(l2,r2);
        getSub(l1,r1)==getSub(l2,r2)?puts("Yes"):puts("No");
    }
    
    return 0;
}

Descripción del tema 2

Dada una cadena de patrón S y una cadena de plantilla P, todas las cadenas solo contienen letras en inglés mayúsculas y minúsculas y números arábigos.

La cadena de plantilla P aparece como una subcadena varias veces en la cadena de patrón S.

Encuentre los subíndices iniciales de todas las posiciones de la cadena de plantilla P en la cadena de patrón S.

Formato de entrada
Introduzca el número entero N en la primera línea, que representa la longitud de la cadena P.

Ingrese la cadena de caracteres P en la segunda línea.

Ingrese el entero M en la tercera línea, que representa la longitud de la cadena S.

Ingrese la cadena S en la cuarta línea.

Formato de salida
Un total de una línea, muestra los subíndices iniciales de todas las posiciones de ocurrencia (los subíndices comienzan a contar desde 0) y los números enteros están separados por espacios.

Rango de datos
1≤N≤10 5
1≤M≤10 6

Muestra de entrada:

3 de sus 5


Salida de muestra:

0 2

Implementación de algoritmos

#include <iostream>
#define read(x) scanf("%d",&x)

using namespace std;

typedef unsigned long long ULL;
const int N=1e5+10,M=1e6+10,R=131;
char ft[M],sn[N]; //ft父串,sn子串
ULL hs[M],h,r[M]={
    
    1};
//hs数组记录ft父串的每个前缀子串的哈希值,h只记录sn子串的哈希值,r记录r^i
"hs[0]代表没有子串,hs[1]代表只有1个字符的前缀子串,hs[k]表示有k个字符的前缀子串"

ULL getSub(int l,int h)
{
    
    
    return hs[h]-hs[l]*r[h-l];
}

int main()
{
    
    
    int n,m;
    read(n);
    scanf("%s",sn+1); //都从下标1开始存储字符串,和hs数组保持一致
    read(m);
    scanf("%s",ft+1);
    //预处理父串
    for (int i=1;i<=m;i++) {
    
    
        hs[i]=hs[i-1]*R+ft[i];
        r[i]=r[i-1]*R;
    }
    //预处理子串
    for (int i=1;i<=n;i++) h=h*R+sn[i];
    
    for (int i=1;i<=m-n+1;i++) {
    
     //子串能到父串的的最后一个位置下标:m-n+1
        if (getSub(i-1,i+n-1)==h) printf("%d ",i-1);   //减一是因为我们的数组下标从1开始,题目里是从1开始
    }         "提前在这里i-1, hs[i+n-1]-hs[i-1] "
    
    return 0;
}

Supongo que te gusta

Origin blog.csdn.net/HangHug_L/article/details/114199310
Recomendado
Clasificación