1.hash字符串

例题:POJ-1200:http://poj.org/problem?id=1200
题意:寻找存在nc个字符的字符串中给定字符串中不同子串数量
注:若无特殊规定,则nc应取13113331以减少冲突
算法思想:将字符串拆分成含有n个字符的子串并将其转化为nc进制数。
字符串相同hash值一定相同,反之不成立(所以要减少冲突)
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+10;
char str[N];
const int maxn=16e6+10;
bool hash[maxn];
int c[128];
int n,nc;
int main(){
    while(cin>>n>>nc>>str){
        memset(hash,false,sizeof(hash));//初始化
        memset(c,0,sizeof(c));
        int len=strlen(str)-n+1;//存在的最大可能组数
        int ans=len;
        int k=0;
        for(int i=0;i<len;i++){//字符与数字对应 
            if(c[str[i]-'a']==0) c[str[i]-'a']=k++;
            if(k==nc) break; //所有字符存在
        }
        for(int i=0;i<len;i++){//每n个字符所形成数字的值与其他是否相同 
            int num=0;
            for(int j=i;j<i+n;j++) num=num*nc+c[str[j]-'a'];//nc进制所表示数值
            num%=maxn;
            if(hash[num]) ans--;//相同则减去 
            else hash[num]=true;
                cout<<ans<<endl;
            }
    }    
    return 0;
}

2.最大最小表示法

字符串同构问题

int getmin(char p[]){
    int plen=strlen(p);
    int i=0,j=1,k=0;    //从i开始k长度和从j开始k长度的字符串相同(i,j表示当前判断的位置)
    while(i<plen&&j<plen&&k<plen){
        int t=p[(i+k)%plen]-p[(j+k)%plen]; //计算相对应位置上两个字符字典序大小 
        if(t==0) k++;
        else{
            if(t>0) i+=k+1;//i开头和j开头的有k个相同的字符    -->最大i,j颠倒
            else j+=k+1;
            if(i==j) j++;
            k=0;
        }        
    }
    return min(i,j);//较小的值就是我们最终开始的位置
}

3.对kmp的next[]数组的探索

例题:http://poj.org/problem?id=1961
题意:寻找字符串前缀的循环串,并输出为循环串时的位置和循环周期
公式:len%(len-next[i])==0,len/(len-next[i]);
参考博客:https://www.cnblogs.com/wuyiqi/archive/2012/01/06/2314078.html
注:在getnextval()函数内边判断边输出

while(j<plen){
        //p[k]为前缀,p[j]位后缀
        if(k==-1||p[k]==p[j]){
            j++;
            k++;
            if(j%(j-k)==0&&j/(j-k)>1)    //k=next[j]
              printf("%d %d\n",j,j/(j-k));//从字符串的第二个开始,看前面是循环串吗,是的话就输出此时的位置,和循环串的周期,周期必须大于1
            if(p[j]!=p[k]) next[j]=k;
            else next[j]=next[k];
        }
        else k=nex[k];
}

最大最小表示法+kmp()

例题:HDU-3374:http://acm.hdu.edu.cn/showproblem.php?pid=3374

4.扩展kmp

1.next[]代码:next[i]表示T[i,m-1]和T的最长公共前缀长度

void getnext(char *str)
{
    int i=0,j,p,len=strlen(str);
    next[0]=len;//初始化next[0]
    while(str[i]==str[i+1]&&i+1<len)//计算next[1]
    i++;
    next[1]=i;
    p=1;//初始化p的位置
    for(i=2;i<len;i++)
    {
        if(next[i-p]+i<next[p]+p)//第一种情况,可以直接得到next[i]的值
        next[i]=next[i-p];
        else//第二种情况,要继续匹配才能得到next[i]的值
        {
            j=next[p]+p-i;
            if(j<0)j=0;//如果i>p+next[p],则要从头开始匹配
            while(i+j<len&&str[j]==str[j+i])//计算next[i]
            j++;
            next[i]=j;
            p=i;//更新p的位置
        }
    }
}

2.extend[]代码:extend[i]表示T与S[i,n-1]的最长公共前缀

代码:

void exkmp(char *s1,char *s2)
{
    int i=0,j,p,len=strlen(s1),l2=strlen(s2);
    getnext(s2);//计算子串的next数组
    while(s1[i]==s2[i]&&i<l2&&i<len)//计算extend[0]
    i++;
    extend[0]=i;
    p=0;//初始化p的位置
    for(i=1;i<len;i++)
    {
        if(next[i-p]+i<extend[p]+p)//第一种情况,直接可以得到ex[i]的值
        extend[i]=next[i-p];
        else//第二种情况,要继续匹配才能得到extend[i]的值
        {
            j=extend[p]+p-i;
            if(j<0)j=0;//如果i>extend[p]+p则要从头开始匹配
            while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算extend[i]
            j++;
            extend[i]=j;
            po=i;//更新po的位置
        }
    }
}

例题:HDU-6629:http://acm.hdu.edu.cn/showproblem.php?pid=6629