字符串的小伙伴

字符串小朋友结交了很多好朋友,他们大多都很乐于助人,帮助字符串解决他的问题

一.字符串哈希

  字符串渴望和整数交好朋友,哈希是一个连接字符串和整数之间的媒介,他可以把字符串和整数相互匹配。哈希是多少?当然这个是要做题的人自己来设置的。

  求一个字符串前缀哈希的公式是:hash【i】=(1ll *hash【i-1】*p+idx(s【i】))%mod;

  其中p自己设为一个六位或八位的素数,mod取1e9+7或者+9,idx(s[i])=s[i]-'a'

  现在咱来讲一下怎么求一个子串的hash,公式是:hash[L...R]=((hash[R]-hash[L-1]*(p的R-L+1次方))%mod+mod)%mod

  常用的几个字符串哈希法:1 unsigned long long hash[N] hash[i]=hash[i-1]*自动取模

              2 单哈希

              3 双哈希,用pair<hash1,hash2>表示一个字符串mod1=1e9+7,mod2=1e9+9

哈希的应用:

1 求s1是不是s2的子串,且在s2中出现了几次

2 求s1和s2的最大公共子串

3 求一个字符串里的最长回文字符串

4 一系列操作后求字典序最小的表示法

哈希代码如下:

//用单hash表示字符串前缀
//并且判断s1在s2中出现了几次
void hashbuild(int n1,int n2){
    for(int i=1;i<=n1;i++){
        hash1[i]=(1ll*hash1[i-1]*p+s1[i]='a')%mod;
    }
    for(int i=1;i<=n2;i++){
        hash2[i]=(1ll*hash2[i-1]*p+s2[i]-'a')%mod;
    } 
    for(int i=1;i<=n2;i++){
        fp[i]=pow(p,i);
    }
    for(int i=n1;i<=n2;i++){
        if((hash2[i]-1ll*hash2[i-n1]*fp[n1]%mod+mod)%mod==hash[n1])ans++;
    }
}

hash 1

//用单hash表示字符串前缀
//并且判断s1在s2中出现了几次
void hashbuild(int n1,int n2){
    for(int i=1;i<=n1;i++){
        hash1[i]=(1ll*hash1[i-1]*p+s1[i]='a')%mod;
    }
    for(int i=1;i<=n2;i++){
        hash2[i]=(1ll*hash2[i-1]*p+s2[i]-'a')%mod;
    } 
    for(int i=1;i<=n2;i++){
        fp[i]=pow(p,i);
    }
    for(int i=n1;i<=n2;i++){
        if((hash2[i]-1ll*hash2[i-n1]*fp[i]%mod+mod)%mod==hash[n1])ans++

二.KMP算法

用来看s1是不是s2的子串

next是前缀后缀相同部分最大值

next:AAA

位置   0 1 2

j=       1 2 3

next   -1 0 1

注意第i个位置看前i-1个,且不要算自己

next值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。

KMP算法

假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置

  • 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
  • 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
    //优化过后的next 数组求法  
    void GetNextval(char* p, int next[])  
    {  
        int pLen = strlen(p);  
        next[0] = -1;  
        int k = -1;  
        int j = 0;  
        while (j < pLen - 1)  
        {  
            //p[k]表示前缀,p[j]表示后缀    
            if (k == -1 || p[j] == p[k])  
            {  
                ++j;  
                ++k;  
                //较之前next数组求法,改动在下面4行  
                if (p[j] != p[k])  
                    next[j] = k;   //之前只有这一行  
                else  
                    //因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]  
                    next[j] = next[k];  
            }  
            else  
            {  
                k = next[k];  
            }  
        }  
    }  
    
返回值是s1在s2中出现的位置
int KmpSearch(char* s, char* p) { int i = 0; int j = 0; int sLen = strlen(s); int pLen = strlen(p); while (i < sLen && j < pLen) { //①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++ if (j == -1 || s[i] == p[j]) { i++; j++; } else { //②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j] //next[j]即为j所对应的next值 j = next[j]; } } if (j == pLen) return i - j+1; else return -1; }

  

原文地址:https://www.cnblogs.com/SUMMER20020929/p/9806278.html