【算法】字符串竞赛模板

102 阅读2分钟

kmp

牛客多校带回退的kmp

#include <bits/stdc++.h>
using namespace std;
int n;
int len[5];
int ans[100005];
char ch[5][100005];
char pro[100005];
int f[100005];
int f2[100005];
int lenp;
int q[100005];
void getfail(char *p,int len){
    f[0]=f[1]=f2[0]=f2[1]=0;
    for(int i=1;i<len;i++){
        int j=f2[i];
        while(j&&p[i]!=p[j])j=f2[j];
        f2[i+1] = f[i+1] = (p[i]==p[j])?j+1:0;
        if(f[i+1]==j+1 && p[i+1]==p[j+1])f[i+1]=f[j+1];
    }
    /**
    带退格的优化kmp:
    当在p[i+1]的位置失配的时候,如果普通kmp的p[i+1]指向p[j+1]
    并且他们相同,那么下次匹配依旧还是失配的,就会继续跳到p[f[j+1]]
    如此下来失配的时候刚好会跳到本该在的位置
    **/
}
void kmp(char *t,char *s,int id){
    getfail(s,len[id]);
    int j=0;
    q[0]=0;
    int r=0;
    for(int i=0;i<lenp;i++){
        if(t[i]=='-'){
            if(r)r--;
            j=q[r];
        }
        else{
            while(j&&s[j]!=t[i])j=f[j];
            if(s[j]==t[i])j++;
            q[++r]=j;
        }
        ans[i] = min(ans[i] , len[id] - q[r]);
    }
}
int main(){
    scanf("%d",&n);
    int temp=0x3f3f3f3f;
    for(int i=0;i<n;i++){
        scanf("%s",ch[i]);
        len[i]=strlen(ch[i]);
        temp=min(temp,len[i]);
    }
    printf("%d\n",temp);
    memset(ans,0x3f,sizeof ans);
    scanf("%s",pro);
    lenp=strlen(pro);
    for(int i=0;i<n;i++){
        kmp(pro,ch[i],i);
    }
    for(int i=0;i<lenp;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

hash

typedef long long ll;
const int maxn = 2e5+10;
char s[maxn];
const int base = 233;
const int mod = 1e9+21;
int po[maxn],hs[maxn];
inline int md(ll x){
    return x>=mod?x%mod:x;
}
inline int geth(int l,int r){
    int ret = hs[r]-md((ll)hs[l-1]*po[r-l+1]);
    return ret<0?ret+mod:ret;
}
int main(){
    scanf("%s",s+1);len=strlen(s+1);
    po[0]=1;
    for(int i=1;i<=len;++i){
        hs[i]=md((ll)hs[i-1]*base + s[i]);
        po[i]=md((ll)po[i-1]*base);
    }
    return 0;
}

manacher

南京现场赛manacher + exkmp

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6+10;
char Ma[maxn<<1];
int Mp[maxn<<1];
void manacher(char s[],int len){
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0;i<len;i++){
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;
    int mx=0,id=0;
    for(int i=0;i<l;i++){
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
        if(i+Mp[i]>mx){
            mx=i+Mp[i];
            id=i;
        }
    }
}
void pre_EKMP(char x[],int m,int next[])
{
    next[0]=m;
    int j=0;
    while(j+1<m && x[j]==x[j+1])j++;
    next[1]=j;
    int k=1;
    for(int i=2; i<m; i++)
    {
        int p=next[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)next[i]=L;
        else
        {
            j=max(0,p-i+1);
            while(i+j<m && x[i+j]==x[j])j++;
            next[i]=j;
            k=i;
        }
    }
}
void EKMP(char x[],int m,char y[],int n,int next[],int extend[])
{
    pre_EKMP(x,m,next);
    int j=0;
    while(j<n && j<m && x[j]==y[j])j++;
    extend[0]=j;
    int k=0;
    for(int i=1; i<n; i++)
    {
        int p=extend[k]+k-1;
        int L=next[i-k];
        if(i+L<p+1)extend[i]=L;
        else
        {
            j=max(0,p-i+1);
            while(i+j<n && j<m && y[i+j]==x[j])j++;
            extend[i]=j;
            k=i;
        }
    }
}
char s[maxn],t[maxn];
int pre[maxn];
int extend[maxn],nxt[maxn];
int main(){
    scanf("%s",s);
    int lens=strlen(s);
    manacher(s,lens);
    for(int i=2;i<2*lens+2;i++){
        if(Mp[i]<2)continue;
        int idx = i/2;
        int id  = idx - Mp[i]/2+1;
        pre[id]++;
        pre[idx+1]--;
    }
    for(int i=1;i<=lens;i++){
        pre[i]+=pre[i-1];
    }
    scanf("%s",t);
    int lent=strlen(t);
    reverse(s,s+lens);
    EKMP(t,lent,s,lens,nxt,extend);
    ll ans=0;
    int j=lens-1;
    for(int i=2;i<=lens;i++){
        ans+=1ll*pre[i]*extend[j];
        j--;
    }
    cout<<ans<<endl;
    return 0;
}

回文自动机

hdu多校第二场的回文串题 pam + hash

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 26;
const int maxn = 3e5+10;
const int base=31;
ull hs[maxn],ht[maxn],po[maxn];
ull geth(int l,int r){return hs[r]-po[r-l+1]*hs[l-1];}
ull gett(int l,int r){return ht[l]-po[r-l+1]*ht[r+1];}
struct pam{
    int nxt[maxn][N],fail[maxn];//在当前状态首尾添加字符的状态,失配跳到的状态
    int len[maxn],S[maxn],ppos[maxn];//状态i表示的回文长度,缓存池,状态对应首次出现的位置
    int cnt[maxn],num[maxn];//状态出现次数,以状态末尾结尾但不包含本条路径的数目
    int last,n,p;//上个状态,总长度,当前状态编号
    int newnode(int l){
        memset(nxt[p],0,sizeof nxt[p]);
        cnt[p]=num[p]=0;
        len[p]=l;
        return p++;
    }
    void init(){
        p=0;
        newnode(0);newnode(-1);
        last=n=0;
        S[n]=-1;
        fail[0]=1;
    }
    int get_fail(int x){
        while(S[n-len[x]-1]!=S[n])x=fail[x];return x;
    }
    void add(int c,int pos){
        c-='a';
        S[++n]=c;
        int cur=get_fail(last);
        if(!nxt[cur][c]){
            int now=newnode(len[cur]+2);
            ppos[now]=pos;
            fail[now]=nxt[get_fail(fail[cur])][c];
            nxt[cur][c]=now;
            num[now]=num[fail[now]]+1;
        }last=nxt[cur][c];cnt[last]++;
    }
    void Count(){
        for(int i=p-1;i>=0;--i)cnt[fail[i]]+=cnt[i];
    }
}p;
char s[maxn];
ull ans[maxn];
int main(){
    po[0]=1;for(int i=1;i<maxn;++i)po[i]=po[i-1]*base;
    while(scanf("%s",s+1)!=EOF){
        p.init();
        int len=strlen(s+1);
        for(int i=1;i<=len;++i){
            p.add(s[i],i);ans[i]=0;hs[i]=hs[i-1]*base+s[i];
        }ht[len]=s[len];
        for(int i=len-1;i;--i)ht[i]=ht[i+1]*base+s[i];
        p.Count();
        for(int i=2;i<p.p;++i){
            int mid = (2*p.ppos[i]-p.len[i]+1)/2;
            int mmid= (p.ppos[i]-p.len[i]+1+mid)/2;
            if(geth(p.ppos[i]-p.len[i]+1,mmid) == 
            gett(mmid+( (mid - p.ppos[i]-p.len[i] ) %2==0),mid) ){
                ans[p.len[i]]+=p.cnt[i];
            }
        }
        for(int i=1;i<=len;++i){
            printf("%llu%c",ans[i]," \n"[i==len]);
        }
    }
    return 0;
}

补充

if(len[now]<=2)half[now]=fail[now];
else{
    int tmp=half[cur];
    while( S[n-len[tmp]-1]!=S[n] || (len[tmp]+2)*2 > len[now] )tmp=fail[tmp];
    half[now]=nxt[tmp][c];
}


    int diff[maxn],snext[maxn];
    /**
    diff[x]表示x的最长回文后缀长度减少了多少
    snext[x]表示fail链上第一个diff[y]!=diff[x]的状态
    series[x]表示x -> snext[x]的链
    */
    int f[maxn],series[maxn];
    f[0]=1;
    for(int i=1;i<=n;++i){
        p.add(s[i],i);
        for(int v=p.last;p.len[v]>0;v=p.snext[v]){
            series[v]=f[i-(p.len[p.snext[v]]+p.diff[v])];
            if(p.diff[v]==p.diff[p.fail[v]])
                (series[v]+=series[p.fail[v]])%=mod;
            (f[i]+=series[v])%=mod;
        }if(i&1)f[i]=0;
    }
    cout<<f[n];


	1 - 31 长度的回文串是否存在
	f[0]=1;
	for(int i=1; i<=n; i++) {
		pam.insert(s[i]-'a');
		for(int v=pam.last; pam.len[v]>0; v=pam.snext[v]) {
			series[v]=f[i-(pam.len[pam.snext[v]]+pam.diff[v])];
			if(pam.diff[v]==pam.diff[pam.next[v]])
				series[v]|=series[pam.next[v]];
			f[i]|=series[v]<<1;
		}
		printf("%u\n",f[i]>>1);
	}

后缀数组

sais

#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
cs int N=4e5;
namespace SA{
    char s[N];
    int sa[N],rk[N],ht[N],len;
    int b[N],wb[N];
    bool t[N<<1];
    inline bool islms(int i,bool *cs t);
    template<class T>
    inline void induced_sort(T s,int len,int siz,int sigma,bool *cs t,int *cs cb,int *cs p);
    template<class T>
    inline void sais(T s,int len,bool *cs t,int *cs b1,int sigma);
    inline void init(char str[],int len);
}
inline bool SA::islms(int i,bool *cs t){
    return i>0&&t[i]&&!t[i-1];
}
template<class T>
inline void SA::induced_sort(T s,int len,int siz,int sigma,bool *cs t,int *cs cb,int *cs p){
    memset(b,0,sigma<<2);
    memset(sa,-1,len<<2);
    for(int re i=0;i<len;++i)++b[s[i]];
    cb[0]=b[0];
    for(int re i=1;i<sigma;++i)cb[i]=cb[i-1]+b[i];
    for(int re i=siz-1;~i;--i)sa[--cb[s[p[i]]]]=p[i];
    for(int re i=1;i<sigma;++i)cb[i]=cb[i-1]+b[i-1];
    for(int re i=0;i<len;++i)if(sa[i]>0&&!t[sa[i]-1])sa[cb[s[sa[i]-1]]++]=sa[i]-1;
    cb[0]=b[0];
    for(int re i=1;i<sigma;++i)cb[i]=cb[i-1]+b[i];
    for(int re i=len-1;~i;--i)if(sa[i]>0&&t[sa[i]-1])sa[--cb[s[sa[i]-1]]]=sa[i]-1;
}
template<class T>
inline void SA::sais(T s,int len,bool *cs t,int *cs b1,int sigma){
    int *cb=b+sigma,siz=0,cnt=0,p=-1;
    t[len-1]=1;
    for(int re i=len-2;~i;--i)t[i]=s[i]==s[i+1]?t[i+1]:(s[i]<s[i+1]);
    for(int re i=1;i<len;++i)if(islms(i,t))b1[siz++]=i;
    induced_sort(s,len,siz,sigma,t,cb,b1);
    for(int re i=siz=0;i<len;++i)if(islms(sa[i],t))sa[siz++]=sa[i];
    memset(sa+siz,-1,(len-siz)<<2);
    for(int re i=0;i<siz;++i){
        re int x=sa[i];
        for(int re j=0;j<len;++j){
            if(p==-1||s[x+j]!=s[p+j]||t[x+j]!=t[p+j]){
                ++cnt;p=x;
                break;
            }
            else if(j>0&&(islms(x+j,t)||islms(p+j,t)))break;
        }
        sa[siz+(x>>1)]=cnt-1;
    }
    for(int re i=len-1,j=len-1;i>=siz;--i)if(~sa[i])sa[j--]=sa[i];
    int *s1=sa+len-siz,*b2=b1+siz;
    if(cnt<siz)sais(s1,siz,t+len,b1+siz,cnt);
    else for(int re i=0;i<siz;++i)sa[s1[i]]=i;
    for(int re i=0;i<siz;++i)b2[i]=b1[sa[i]];
    induced_sort(s,len,siz,sigma,t,cb,b2);
}
inline void SA::init(char str[],int len1){
    memset(ht,0,sizeof ht);
    memset(rk,0,sizeof rk);
    memset(b,0,sizeof b);
    memset(wb,0,sizeof wb);
    memset(t,0,sizeof t);
    sais(s+1,len1+1,t,wb,128);
    rk[0]=0,sa[0]=len1+1;
    for(int re i=1;i<=len1;++i)rk[++sa[i]]=i;
    for(int re i=1,k=0,j=0;i<len1;ht[rk[i++]]=k)
    for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}
/***/
int RMQ[N],mm[N];
int best[18][N];
void initRMQ(int n){
    mm[0]=-1;
    for(int i=1;i<=n;++i)
        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
    for(int i=1;i<=n;i++)best[0][i]=i;
    for(int i=1;i<=mm[n];i++)
        for(int j=1;j+(1<<i)-1<=n;j++){
            int a=best[i-1][j];
            int b=best[i-1][j+(1<<(i-1))];
            if(RMQ[a]<RMQ[b])best[i][j]=a;
            else best[i][j]=b;
        }
}
int askRMQ(int a,int b){
    int t=mm[b-a+1];
    b-=(1<<t)-1;
    a=best[t][a];b=best[t][b];
    return RMQ[a]<RMQ[b]?a:b;
}
int lcp(int a,int b){
    //a=SA::rk[a];b=SA::rk[b];
    if(a>b)swap(a,b);
    return SA::ht[askRMQ(a+1,b)];
}
/***/

下一位置不确定的SA做法

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
char s[maxn+50];
int sa[maxn+50],rk[maxn+50];
int t[maxn+50],t2[maxn+50],c[maxn+50];
int nx[maxn+5][19];
int len,k;
queue<int> q[maxn+5];
void getsa(int m)//m表示最大字符的编码
{
    memset(t,-1,sizeof(t));
    memset(t2,-1,sizeof(t2));
    int *x=t,*y=t2;
    for(int i=0;i<m;++i) c[i]=0;
    for(int i=0;i<len;++i) c[x[i]=s[i]]++;
    for(int i=1;i<m;++i) c[i]+=c[i-1];
    for(int i=len-1;i>=0;--i) sa[--c[x[i]]]=i;
    for(int j=0,k=1;k<=len;k<<=1,++j)
    {
        /*int p=0;
        for(int i=len-k;i<len;++i) y[p++]=i;
        for(int i=0;i<len;++i) if(sa[i]>=k) y[p++]=sa[i]-k;*/

        int p=0;
        for(int i=0;i<len;++i) q[nx[i][j]].push(i);
        for(int i=0;i<len;++i)
            while(!q[sa[i]].empty())
            {
                y[p++]=q[sa[i]].front();
                q[sa[i]].pop();
            }

        for(int i=0;i<m;++i) c[i]=0;
        for(int i=0;i<len;++i) c[x[y[i]]]++;
        for(int i=0;i<m;++i) c[i]+=c[i-1];
        for(int i=len-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1,x[sa[0]]=0;
        for(int i=1;i<len;++i)
            if(y[sa[i-1]]==y[sa[i]]&&y[nx[sa[i-1]][j]]==y[nx[sa[i]][j]]) x[sa[i]]=p-1;else x[sa[i]]=p++;
        if(p>=len) break;
        m=p;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int cas=1;cas<=T;++cas)
    {
        printf("Case #%d: ",cas);
        scanf("%d",&len);
        scanf("%s",s);
        for(int i=0;i<len;++i) nx[i][0]=(1LL*i*i+1)%len;
        for(int j=1;j<=18;++j)
            for(int i=0;i<len;++i) nx[i][j]=nx[nx[i][j-1]][j-1];
        getsa('9'+1);
        int pos=sa[len-1];
        for(int i=1;i<=len;++i,pos=nx[pos][0]) printf("%c",s[pos]);
        printf("\n");
    }
   // for(int i=0;i<n;++i) printf("%d ",sa[i]);printf("\n");
   // for(int i=0;i<n;++i) printf("%d ",rk[i]);printf("\n");
   // for(int i=0;i<n;++i) printf("%d ",height[i]);printf("\n");
    return 0;

}

两串相同子串个数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 4e5+10;
char s1[maxn],s2[maxn];
int len1,len2;
pair<int,ll> stk[maxn];
struct SA{
    int rk[maxn],sa[maxn],height[maxn];
    int t1[maxn],t2[maxn],c[maxn];
    bool cmp(int *s,int a,int b,int l){
        return s[a]==s[b]&&s[a+l]==s[b+l];
    }
    void Sa(char *s,int n,int m){
        n++;
        int i,j,p,*x=t1,*y=t2;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=s[i]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1){
            p=0;
            for(i=n-j;i<n;i++)y[p++]=i;
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;i<m;i++)c[i]+=c[i-1];
            for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;x[sa[0]]=0;
            for(i=1;i<n;i++)x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
            if(p>=n)break;m=p;
        }
        int k=0;n--;
        for(i=0;i<=n;i++)rk[sa[i]]=i;
        for(i=0;i<n;i++){
            if(k)k--;
            j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    void solve(int n){
        ll ret=0;
        int sum[maxn];sum[0]=0;
        for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]<len1);
        int top=0;
        stk[0]=make_pair(1,0);
        for(int i=1;i<=n;i++){
            while(top &&height[ stk[top].first ]>height[i])top--;++top;
            stk[top]=make_pair(i,(sum[i-1]-sum[stk[top-1].first-1])*height[i]+stk[top-1].second);
            if(sa[i]>len1)ret+=stk[top].second;
        }top=0;
        for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(sa[i]>len1);
        for(int i=1;i<=n;i++){
            while(top &&height[ stk[top].first ]>height[i])top--;top++;
            stk[top]=make_pair(i,(sum[i-1]-sum[stk[top-1].first-1])*height[i]+stk[top-1].second);
            if(sa[i]<len1)ret+=stk[top].second;
        }cout<<ret;
    }
}sa;
char s[maxn];
int main(){
    scanf("%s%s",s1,s2);
    len1=strlen(s1);len2=strlen(s2);
    int n=0;
    for(int i=0;i<len1;i++)s[n++]=s1[i];
    s[n++]='$';
    for(int i=0;i<len2;i++)s[n++]=s2[i];
    s[n]=0;
    sa.Sa(s,n,128);
    sa.solve(n);
    return 0;
}

AC自动机

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e6+10;

char t[151][80];
int cnt[151];
char s[maxn];
int n;
struct trie{
    int nxt[maxn][26],fail[maxn];
    int ed[maxn];
    int rt,L;
    int newnode(){
        memset(nxt[L],-1,sizeof nxt[L]);
        ed[L++]=0;
        return L-1;
    }
    void init(){
        L=0;rt=newnode();
    }
    void insert(int id,char buf[]){
        int len=strlen(buf);
        int now=rt;
        for(int i=0;i<len;++i){
            if(nxt[now][buf[i]-'a']==-1)nxt[now][buf[i]-'a']=newnode();
            now=nxt[now][buf[i]-'a'];
        }ed[now]=id;
    }
    void build(){
        queue<int>q;
        fail[rt]=rt;
        for(int i=0;i<26;++i){
            if(nxt[rt][i]==-1)nxt[rt][i]=rt;
            else {
                fail[nxt[rt][i]]=rt;
                q.push(nxt[rt][i]);
            }
        }
        while(!q.empty()){
            int now = q.front();q.pop();
            for(int i=0;i<26;++i){
                if(nxt[now][i]==-1)nxt[now][i]=nxt[fail[now]][i];
                else{
                    fail[nxt[now][i]]=nxt[fail[now]][i];
                    q.push(nxt[now][i]);
                }
            }
        }
    }
    void query(char buf[]){
        int len=strlen(buf);
        int now = rt;
        for(int i=0;i<len;++i){
            now = nxt[now][buf[i]-'a'];
            int tmp=now;
            while(tmp!=rt){
                cnt[ed[tmp]]++;
                tmp=fail[tmp];
            }
        }
    }
}ac;
int main(){
    while(cin>>n&&n){
        ac.init();
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;++i){
            scanf("%s",t[i]);
            ac.insert(i,t[i]);
        }ac.build();
        scanf("%s",s);
        ac.query(s);
    }
    return 0;
}

SAM

最短不重复子串4合1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf  = 0x3f3f3f3f;
const int maxn = 4e3+10;
char a[maxn],b[maxn];
int lena,lenb;
struct SEM{
    int nxt[maxn][26];
    int len;
    inline void init(char *s,int l){
        len=l;
        for(int i=0;i<26;i++)nxt[len][i]=len;
        for(int i=len-1;i>=0;i--){
            memcpy(nxt[i],nxt[i+1],sizeof nxt[i+1]);
            nxt[i][s[i]-'a']=i;
        }
    }
}AA,BB;
struct SAM{
    int pa[maxn<<1],son[maxn<<1][27];
    int deep[maxn<<1],cnt,root,last;
    inline int newnode(int _deep){
        deep[++cnt]=_deep;
        return cnt;
    }
    inline void sam(int alp){
        int np=newnode(deep[last]+1);
        int u=last;
        memset(son[np],0,sizeof son[np]);
        while(u&&!son[u][alp])son[u][alp]=np,u=pa[u];
        if(!u)pa[np]=root;
        else{
            int v=son[u][alp];
            if(deep[v]==deep[u]+1)pa[np]=v;
            else{
                int nv=newnode(deep[u]+1);
                memcpy(son[nv],son[v],sizeof son[v]);
                pa[nv]=pa[v],pa[v]=pa[np]=nv;
                while(u&&son[u][alp]==v)son[u][alp]=nv,u=pa[u];
            }
        }last=np;
    }
    inline void pre(){
        cnt=0;
        memset(son[1],0,sizeof son[1]);
        root=last=newnode(0);
    }
}A,B;
int solve1(){
    int ret=inf;
    for(int i=0;i<lena;i++){
        int pos=B.root;
        for(int j=i;j<lena;j++){
            if(B.son[pos][a[j]-'a'])pos=B.son[pos][a[j]-'a'];
            else{
                ret=min(ret,j-i+1);break;
            }
        }
    }return ret==inf?-1:ret;
}
int solve2(){
    int ret=inf;
    for(int i=0;i<lena;i++){
        int pos=0;
        for(int j=i;j<lena;j++){
            if(BB.nxt[pos][a[j]-'a']==BB.len){
                ret=min(ret,j-i+1);break;
            } else pos=BB.nxt[pos][a[j]-'a']+1;
        }
    }return ret==inf?-1:ret;
}
int solve3(){
    int ret=inf;
    int dp[maxn];//dp[i]表示状态i能够接受的最短A的子序列
    memset(dp,0x3f,sizeof dp);
    dp[1]=0;
    for(int i=0;i<lena;i++){
        for(int j=B.root;j<=B.cnt;j++){
            if(B.son[j][a[i]-'a']){
                dp[B.son[j][a[i]-'a']]=min(dp[B.son[j][a[i]-'a']],dp[j]+1);
            } else ret=min(ret,dp[j]+1);
        }
    }return ret==inf?-1:ret;
}
struct node{
    int a,b,step;
    node(int a=0,int b=0,int step=0):a(a),b(b),step(step){}
};
bool vis[maxn][maxn]={0};
int solve4(){
    queue<node> q;
    q.push(node(0,0,0));
    while(!q.empty()){
        node now=q.front();q.pop();
        //cout<<now.a<<" "<<now.b<<" "<<now.step<<endl;
        for(int i=0;i<26;i++){
            if(AA.nxt[now.a][i]==AA.len)continue;
            if(vis[AA.nxt[now.a][i]][BB.nxt[now.b][i]])continue;
            if(BB.nxt[now.b][i]==BB.len)return now.step+1;
            vis[AA.nxt[now.a][i]][BB.nxt[now.b][i]]=1;
            q.push(node(AA.nxt[now.a][i]+1,BB.nxt[now.b][i]+1,now.step+1));
        }
    }return -1;
}
int main(){
    scanf("%s%s",a,b);
    lena=strlen(a);
    lenb=strlen(b);
    A.pre();B.pre();
    for(int i=0;i<lena;i++)A.sam(a[i]-'a');
    for(int i=0;i<lenb;i++)B.sam(b[i]-'a');
    AA.init(a,lena);BB.init(b,lenb);
    printf("%d\n",solve1());
    printf("%d\n",solve2());
    printf("%d\n",solve3());
    printf("%d\n",solve4());
    return 0;
}

允许3次失配的子串匹配

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int tran(char c){
    if(c=='A')return 0;if(c=='T')return 1;if(c=='C')return 2;if(c=='G')return 3;
}
char s1[maxn],s2[maxn];
int len1,len2;
struct SAM{
    int pa[maxn<<1],son[maxn<<1][4];
    int deep[maxn<<1],cnt,root,last;
    int sum[maxn<<1],topo[maxn<<1];
    int r[maxn<<1];
    inline void pre(){
        memset(r,0,sizeof r);
    }
    inline void toposort(){
        int tmp=root;
        for(int a=0;a<len1;a++){
            tmp=son[tmp][tran(s1[a])];
            r[tmp]=1;
        }
        memset(sum,0,sizeof sum);
        for(int a=1;a<=cnt;a++)sum[deep[a]]++;
        for(int a=1;a<=deep[last];a++)sum[a]+=sum[a-1];
        for(int a=1;a<=cnt;a++)topo[sum[deep[a]]--]=a;
        for(int a=cnt;a>=1;a--)r[pa[topo[a]]] += r[topo[a]];
    }
}A;
int ans;
void dfs(int pos1,int pos2,int lim){
    if(lim>3)return;
    if(pos2==len2){
        ans+=A.r[pos1];return;
    }
    for(int a=0;a<4;a++){
        if(A.son[pos1][a]){
            dfs(A.son[pos1][a],pos2+1,lim+(a!=tran(s2[pos2])));
        }
    }
}
int main(){
    int t;cin>>t;
    while(t--){
        ans=0;
        scanf("%s%s",s1,s2);
        A.pre();
        len1=strlen(s1);len2=strlen(s2);
        for(int i=0;i<len1;i++)A.sam(tran(s1[i]));
        A.toposort();
        dfs(1,0,0);
        printf("%d\n",ans);
    }
    return 0;
}

第k小子串

/**T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+1000;
char s[maxn];
int pa[maxn<<1],son[maxn<<1][27];
int deep[maxn<<1],cnt,root,last;
int sum[maxn<<1],topo[maxn<<1];
int r[maxn<<1],rr[maxn<<1];
inline void sam(int alp){
    r[np]=1;
}
inline void toposort(){
    for(int a=1;a<=cnt;a++)sum[deep[a]]++;
    for(int a=1;a<=deep[last];a++)sum[a]+=sum[a-1];
    for(int a=1;a<=cnt;a++)topo[sum[deep[a]]--]=a;
}
int T,K;
int main(){
    scanf("%s%d%d",s,&T,&K);
    pre();
    memset(son[1],0,sizeof son[1]);
    int len=strlen(s);
    for(int a=0;a<len;a++){
        sam(s[a]-'a');
    }
    toposort();
    for(int i=cnt;i;i--){
        if(T)r[pa[topo[i]]] += r[topo[i]];
        else r[topo[i]]=1;
    }
    r[root]=0;
    for(int i=cnt;i;i--){
        rr[topo[i]]=r[topo[i]];
        for(int j=0;j<26;j++){
            rr[topo[i]]+=rr[son[topo[i]][j]];
        }
    }
    int now=root;
    if(K>rr[root])return puts("-1");
    while(K){
        K-=r[now];
        if(K<=0)break;
        for(int i=0;i<26;i++){
            if(son[now][i]){
                if(rr[son[now][i]]>=K){
                    putchar(i+'a');
                    now=son[now][i];
                    break;
                }
                else K-=rr[son[now][i]];
            }
        }
    }
    puts("");
    return 0;
}

出现次数在[A,B]之间的子串个数

    int tmp=root;
    for(int a=0;a<len;a++){
        tmp=son[tmp][s[a]-'A'];
        r[tmp]=1;
    }
    /**将所有状态的right集合大小赋值为1**/
    for(int a=cnt;a>=1;a--)r[pa[topo[a]]] += r[topo[a]];
    long long ans=0;
    for(int a=cnt;a>=1;a--){
        if(r[topo[a]]>=A && r[topo[a]]<=B){
            ans+=deep[topo[a]] - deep[pa[topo[a]]];
            /**right集合中不同子串个数为deep[p] - deep[pa[p]]]**/
        }
    }
    cout<<ans<<endl;

两串LCS

        for(int a=0;a<len;a++)sam(s[a]-'a');
        for(int a=1,l=0,p=1;a<=m;a++){
            int x=t[a]-'a';
            if(son[p][x])p=son[p][x],l++;
            else{
                for(;p&&!son[p][x];p=pa[p]);
                if(!p)l=0,p=1;
                else l=deep[p]+1,p=son[p][x];
            }
            /**
            p表示当前在哪个状态,l表示当前状态匹配的最长子串
            deep[p]表示在p状态下,得到的最长子串的长度
            **/
            ans=max(ans,l);
        }
        printf("%d\n",ans);

多个串的LCS

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
char s[maxn];
int pa[maxn<<1],son[maxn<<1][27];
int deep[maxn<<1],cnt,root,last;
int sum[maxn<<1],topo[maxn<<1];
int arr[maxn<<1],ned[maxn<<1];
inline int newnode(int _deep){
    deep[++cnt]=_deep;
    return cnt;
}
inline void sam(int alp){
}
inline void toposort(){
    for(int a=1;a<=cnt;a++)sum[deep[a]]++;
    for(int a=1;a<=deep[last];a++)sum[a]+=sum[a-1];
    for(int a=1;a<=cnt;a++)topo[sum[deep[a]]--]=a;
}
inline void pre(){
    root=last=newnode(0);
}
int main(){
    scanf("%s",s);
    pre();
    memset(son[1],0,sizeof son[1]);
    int len=strlen(s);
    for(int a=0;a<len;a++)sam(s[a]-'a');
    toposort();
    for(int a=1;a<=cnt;a++)ned[a]=deep[a];
    while(scanf("%s",s+1)!=EOF){
        int m=strlen(s+1);
        for(int a=1,l=0,p=1;a<=m;a++){
            int x=s[a]-'a';
            if(son[p][x])p=son[p][x],l++;
            else{
                for(;p && !son[p][x];p=pa[p]);
                if(!p)l=0,p=1;
                else l=deep[p]+1,p=son[p][x];
            }
            arr[p]=max(arr[p],l);
        }
        for(int a=cnt;a>=1;a--){
            int p=topo[a];
            ned[p]=min(ned[p],arr[p]);
            if(arr[p]&&pa[p])arr[pa[p]]=deep[pa[p]];
            arr[p]=0;
        }
    }
    int ans=0;
    for(int a=1;a<=cnt;a++)ans=max(ans,ned[a]);
    printf("%d\n",ans);
    return 0;
}

区间[L,R]内不同子串个数

inline void sam(int alp){
    tot+=deep[np]-deep[pa[np]];
}
inline void pre(){
    cnt=0;
    memset(son[1],0,sizeof son[1]);
    root=last=newnode(0);
    tot=0;
}
for(int i=0;i<len;i++){
    pre();
    for(int j=i;j<len;j++){
        sam(s[j]-'a');
        ans[i][j]=tot;
    }
}
int q;scanf("%d",&q);
while(q--){
    int l,r;
    scanf("%d%d",&l,&r);
    printf("%d\n",ans[l-1][r-1]);
}

统计长度为[1..l]的子串最多出现次数

    int tmp=root;
    for(int a=0;a<len;a++){
        tmp=son[tmp][s[a]-'a'];
        r[tmp]=1;
    }
    /***将原串前缀状态的right集合大小设为1,
    相当于在后缀树中将后缀结点标记为1**/
    toposort();
    /**deep[a] > deep[pa[a]],所以排序,
    用a状态更新pa[a]的状态,只考虑结点的deep便可
    **/
    for(int a=cnt;a>=1;a--)r[pa[topo[a]]] += r[topo[a]];/**right表示状态在整个串出现次数**/
    for(int a=1;a<=cnt;a++)f[deep[a]] = max(f[deep[a]] , r[a]);/**只更新最长串**/
    for(int a=len;a>=1;a--)f[a-1]=max(f[a],f[a-1]);/**长串更新短串:短串包含在长串里面**/
    for(int i=1;i<=len;i++)printf("%d\n",f[i]);

最小表示法

int minpre(char s[]){
	int m = strlen(s);
	memcpy(s + m, s,m*sizeof(char));
	int i = 0,j = 1;//i表示从i往后是有可能的,j表示下一个试探的位置 
	while(j < m && i < m) {
		int k;
		for (k = 0;s[i + k] == s[j + k] && k < m; k++)
		if(k == m ) return min(i,j);//如果相等了,那最小的就是答案 
		if(s[i + k] > s[j + k]) i = i + k + 1;//i到i+k开始的都不可能 
		else (j = j + k + 1);//同上 
	    if (i == j) j++;//刚好相等,重新比较 
	    
	}
	if(i < m) return i;
	else return j;
}
int main(){
    char s[20005];
	int n;
	scanf("%d",&n);
	while(n--){
		scanf("%s",s);
	    printf("%d\n",minpre(s) + 1);
	}	
}