# 7.21 学习笔记

时间:2021-08-25
本文章向大家介绍# 7.21 学习笔记,主要包括# 7.21 学习笔记使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1 字符串hash

1.1 Codeforces 955D Scissors

我们设 \(L_i\) 表示在第二个字符串(以下称该字符串为 \(t\) )长度为 \(i\) 的前缀在第一个字符串(以下称该字符串为 \(s\) )出现最高左的位置是哪个位置。不难发现,去掉无意义的位置,\(L\) 这个数组一定是递增的,所以我们可以考虑用双指针来完成这个事情。

同样,我们可以预处理出 \(R_i\) 表示长度为 \(i\) 的后缀在 \(s\) 中出现最靠右的位置。

这样,\(t\) 被分成两半的情况就分别讨论完了,需要注意的是还有一种情况,就是你这个 \(t\) 可能存在于 \(s\) 非常靠左的位置或非常靠右的位置,且 \(s\) 的长度要小于 \(t\) ,在这种情况下,上面是讨论不到的。所以我们需要额外讨论一下 \(t\)\(s\) 中出现的情况。这种情况我们直接取前缀的一段和后缀的一段就可以了。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 500010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const ull mod=13331;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}
template<typename T> inline T Max(T a,T b){
    return a<b?b:a;
}

template<typename T> inline T Min(T a,T b){
    return a<b?a:b;
}

int n,m,L[N],R[N],k;
char s[N],t[N];
ull hs[N],ht[N],mpow[N];

inline ull GetHash(ull *h,int l,int r){
    return h[r]-h[l-1]*mpow[r-l+1];
}

inline void prework(){
    int l,r;l=1;r=1;
    int minn=Min(m,k);
    while(r-l+1<=minn&&r<=n-k){
        ull ha=GetHash(hs,l,r);
        ull hb=GetHash(ht,1,r-l+1);
        if(ha==hb){
            L[r-l+1]=r;
            if(l>1) l--;
            else r++;
        }
        else{l++;r++;}
    }
    for(int i=1;i<=m;i++) if(L[i]<k) L[i]=0;
    l=r=k;
    while(r-l+1<=minn&&r<=n-k&&L[r-l+1]==0){
        ull ha=GetHash(hs,l,r);
        ull hb=GetHash(ht,1,r-l+1);
        if(ha==hb){
            L[r-l+1]=r;
            if(l>1) l--;
            else r++;
        }
        else{l++;r++;}
    }
    l=r=n;
    while(r-l+1<=minn&&l>=k+1){
        ull ha=GetHash(hs,l,r);
        ull hb=GetHash(ht,m-r+l,m);
        if(ha==hb){
            R[r-l+1]=l;
            if(r<n) r++;
            else l--;
        }
        else{l--;r--;}
    }
    for(int i=1;i<=m;i++) if(R[i]>n-k+1) R[i]=0;
    l=r=n-k+1;
    while(r-l+1<=minn&&l>=k+1&&R[r-l+1]==0){
        ull ha=GetHash(hs,l,r);
        ull hb=GetHash(ht,m-r+l,m);
        if(ha==hb){
            R[r-l+1]=l;
            if(r<n) r++;
            else l--;
        }
        else{l--;r--;}
    }
    for(int i=1;i<=m;i++){
        if(!L[i]) L[i]=-INF;
        if(!R[i]) R[i]=INF;
    }
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);read(k);
    int maxx=Max(n,m);
    mpow[0]=1;for(int i=1;i<=maxx;i++) mpow[i]=mpow[i-1]*mod;
    scanf("%s%s",s+1,t+1);
    for(int i=1;i<=n;i++) hs[i]=hs[i-1]*mod+s[i]-'a';
    for(int i=1;i<=m;i++) ht[i]=ht[i-1]*mod+t[i]-'a';
    prework();
    // printf("L: ");for(int i=1;i<=m;i++) printf("%d ",L[i]);putchar('\n');
    // printf("R: ");for(int i=1;i<=m;i++) printf("%d ",R[i]);putchar('\n'); 
    for(int i=1;i<=m;i++){
        if(L[i]==-INF||R[m-i]==INF) continue;
        else if(L[i]<R[m-i]){
            printf("Yes\n");
            printf("%d %d\n",L[i]-k+1,R[m-i]);
            return 0;
        }
    }
    ull all=GetHash(ht,1,m);
    for(int i=1;i<=n-m+1;i++){
        ull now=GetHash(hs,i,i+m-1);
        if(all==now){
            printf("Yes\n");
            printf("1 %d\n",n-k+1);
            return 0;
        }
    }
    printf("No\n");
    return 0;
}
/*
不能够从两边避免 k ,但是需要防止 k 的情况。
51 13 11
cbcbbcbbbbbcccbcccbbbcbbbbbbbbbbbbcbbbcbbcbbbbcbbbb
bbbbbccbcbbcb
ans:
Yes
3 38
*/

1.2 Codeforces 985F Isomorphic Strings

这个题我们只需要改变 hash 的方式,把每一个字符单独拿出来,用出现次数来做 hash,这样就可以完成这道题了。同时我们可以利用 multiset 来判断相等,这样可以避免 hash 出错。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 300000
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const int base=114514;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,m,mpow[N],h[26][N];
char s[N];

inline int GetHash(int *h,int l,int r){
    // printf("l:%d r:%d r-l+1:%d\n",l,r,r-l+1);
    return ((h[r]-h[l-1]*mpow[r-l+1]%mod)+mod)%mod;
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(m);scanf("%s",s+1);
    mpow[0]=1;for(int i=1;i<N;i++) mpow[i]=(mpow[i-1]*base)%mod;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=25;j++) h[j][i]=(h[j][i-1]*base+1+(s[i]-'a'==j))%mod;
    }
    for(int i=1;i<=m;i++){
        int a,b,c;read(a);read(b);read(c);
        multiset<int> s,t;
        for(int j=0;j<=25;j++){
            // printf("%lld %lld\n",GetHash(h[j],a,a+c-1),GetHash(h[j],b,b+c-1));
            s.insert(GetHash(h[j],a,a+c-1));
            t.insert(GetHash(h[j],b,b+c-1));
        }
        printf("%s\n",s==t?"YES":"NO");
    }
}//

1.3 二维hash darkbzoj2351

二维 hash 其实和一维 hash 一样,也就是说我们拿第一个质数横着做一遍 hash,然后换一个质数,把 hash 值当成要做 hash 的值,竖着在做一遍 hash,这样就可以做了。

代码:

// #include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<map>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 201
#define M 2010
using namespace std;

const int INF=0x3f3f3f3f;
const ull mod=131;
const ull base=13331;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Max(T a,T b){
    return a<b?b:a;
}

ull mpow[M],bpow[M];

ull d[M][M],f[M][M];
// set<ull> S;
map<ull,int> S;

int m,n,a,b,q,ans[N*10];
char s[M];

int main(){


    read(n);read(m);read(a);read(b);int maxx=Max(n,m);
    mpow[0]=bpow[0]=1;
    for(int i=1;i<=maxx;i++){
        mpow[i]=mpow[i-1]*mod;bpow[i]=bpow[i-1]*base;
    }
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++){
            d[i][j]=d[i][j-1]*mod+s[j]-'0';
        }
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            d[i][j]+=d[i-1][j]*base;
    for(int i=1;i<=n-a+1;i++)
        for(int j=1;j<=m-b+1;j++){
            int x=i+a-1,y=j+b-1;
            ull now=d[x][y]-d[x][j-1]*mpow[y-j+1];
            ull now2=d[i-1][y]-d[i-1][j-1]*mpow[y-j+1];
            ull nowans=now-now2*bpow[x-i+1];
            // if(S.count(nowans)) ans[S[nowans]]=1;
            S[nowans]=1;
        }
    read(q);
    for(int i=1;i<=q;i++){
        ull nowans=0;
        for(int j=1;j<=a;j++){
            ull now=0;
            scanf("%s",s+1);
            for(int k=1;k<=b;k++){
                now*=mod;now+=s[k]-'0';
            }
            nowans*=base;nowans+=now;
        }
        // printf("i:%d nowans:%llu\n",i,nowans);
        // S[nowans]=i;
        if(S.count(nowans)) ans[i]=1;
    }
    for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}

1.4 UVA1401 Remember the Word

显然是一个 dp 。我们设 \(f_i\) 表示考虑完前 \(i\) 个字符的方案数,那么转移就是:

\[f_i=\sum\limits_{s_{i+1,j}\in dictionary}f_j+1 \]

我们发现这个东西的复杂度在于判断是否在字典里,所以我们不妨反过来做:

\(f_i\) 表示考虑完 \(s_{i,len}\) 的方案数,转移和上面差不多,至于判断是否在字典里,我们可以建一棵 Trie 树来帮助我们判断,因为 Trie 树的节点深度不会超过 \(100\) ,所以复杂度为 \(O(100n)\)

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 400010
#define M 110
using namespace std;

const int INF=0x3f3f3f3f;
const int mod=20071027;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int ch[N][26],tot,End[N],n,f[N];
char s[N],t[M];

inline void Insert(char *s){
    int len=strlen(s+1),p=0;
    for(int i=1;i<=len;i++){
        int c=s[i]-'a';
        if(!ch[p][c]) ch[p][c]=++tot;
        p=ch[p][c];
    }
    End[p]=1;
}



int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    int test=0;
    while(scanf("%s",s+1)!=EOF){
        test++;
        memset(ch,0,sizeof(ch));
        memset(End,0,sizeof(End));
        read(n);
        for(int i=1;i<=n;i++){
            scanf("%s",t+1);Insert(t);
        }
        memset(f,0,sizeof(f));
        int len=strlen(s+1);f[len+1]=1;
        for(int i=len;i>=1;i--){
            int p=0;
            for(int j=i;j<=len;j++){
                int c=s[j]-'a';
                if(!ch[p][c]) break;
                p=ch[p][c];if(End[p]) (f[i]+=f[j+1])%=mod;
            }
        }
        printf("Case %d: ",test);
        printf("%d\n",f[1]);
    }
    return 0;
}

1.5 UVA1519 Dictionary Size

看到前缀和后缀我们首先想到 Trie 树。我们正向建一棵 Trie 树,反向建一棵 Trie 树。那么答案我们首先认为是两颗 Trie 树的节点个数减 \(1\) 的乘积。

不难发现,有一些字符串我们没有统计,就是那些长度为 \(1\) 的字符串,这是因为我们上面的操作只统计了长度大于等于 \(2\) 的字符串。与此同时,我们有一些字符串被重复统计,不难发现,重复统计的次数是每一个字符在 Trie 树上的出现次数之积,减去就可以了。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define int long long
#define ull unsigned long long
#define N 400010
#define M 50
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

char s[M];
int n,ans,c[M];

struct Trie{
    int p[N][26],tot;
    int cnt[M];
    inline Trie(){tot=0;}
    inline void Insert(char *s){
        int now=0;int len=strlen(s);
        for(int i=0;i<len;i++){
            int k=s[i]-'a';
            if(!p[now][k]){
                p[now][k]=++tot;
                if(i){
                    cnt[k]++;
                }
            }
            now=p[now][k];
        }
    }
    inline void clear(){
        tot=0;memset(p,0,sizeof(p));
        memset(cnt,0,sizeof(cnt));
    }
};
Trie t1,t2;

signed main(){
    while(cin>>n){
        for (int i = 0; i < n; i++) {
            scanf("%s", s);
 
            int n = strlen(s);
            if (n == 1)
                c[s[0] - 'a'] = 1;
            t1.Insert(s);
            reverse(s, s + n);
            t2.Insert(s);
        }
        ans=t1.tot*t2.tot;
        for(int i=0;i<=25;i++){
            if(c[i]) ans++;
            ans-=(t1.cnt[i]*t2.cnt[i]);
        }
        printf("%lld\n",ans);
        t1.clear();t2.clear();
        ans=0;memset(c,0,sizeof(c));
    }
}

2 AC自动机

2.1 P4052 [JSOI2007]文本生成器

首先关注到不出现,不出现的话考虑用 AC 自动机,主要是借助 Trie 树这个结构。我们简单的取一下补集。

既然不能出现,那么也就是说不能做到结束节点,更进一步,我们在 Trie 树上走,我们也不能走到一个节点,其所表示的字符串的后缀是这 \(n\) 个字符串中的一个。换句话说,我们要把所有满足后缀不合法的节点标记一下,怎么标记?我们在建立 AC 自动机的时候把这个节点的 end 与其后缀链接的 end 或一下就可以了。

然后就比较套路了,我们设 \(f_{i,j}\) 表示走了 \(i\) 步,在 Trie 图上的 \(j\) 节点,方案数。转移就枚举下一步要去哪里即可。然后把每个位置上走了 \(m\) 步的方案数累加就可以了。

值得一提的是,Trie 图并不是 DAG ,不用担心在 Trie 图上做 dp 的正确性。

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 110
#define M 6100
using namespace std;

const int INF=0x3f3f3f3f;
const int mod=1e4+7;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

struct node{
    int ch[26],end,fail;
};

int f[N][M],n,m;
char s[N];

struct AC{
    node p[M];int tot;
	inline AC(){memset(p,0,sizeof(p));tot=0;}
    inline void Insert(char *s){
        int len=strlen(s),now=0;
        for(int i=0;i<=len-1;i++){
            int k=s[i]-'A';
            if(!p[now].ch[k]) p[now].ch[k]=++tot;
            now=p[now].ch[k];
        }
        p[now].end=1;
    }
    inline void GetFail(){
        queue<int> q;
		while(q.size()) q.pop();
        for(int i=0;i<=25;i++) if(p[0].ch[i]) q.push(p[0].ch[i]);
        while(q.size()){
            int top=q.front();q.pop();
            for(int i=0;i<=25;i++){
                if(p[top].ch[i]){
                    p[p[top].ch[i]].fail=p[p[top].fail].ch[i];
                    p[p[top].ch[i]].end|=p[p[p[top].fail].ch[i]].end;
                    q.push(p[top].ch[i]);
                }
                else p[top].ch[i]=p[p[top].fail].ch[i];
            }
        }
    }
    inline void DP(){
        f[0][0]=1;
        for(int i=1;i<=m;i++){
            for(int j=0;j<=tot;j++){
                for(int k=0;k<=25;k++){
                    if(p[p[j].ch[k]].end==1) continue;
                    (f[i][p[j].ch[k]]+=f[i-1][j])%=mod;
                }
            }
        }
    }
    inline int GetAns(){
        int ans=0;
        for(int i=0;i<=tot;i++){
            ans+=f[m][i];ans%=mod;
        }
        return ans;
    }
};
AC ac;

inline int ksm(int a,int b,int mod){
	int res=1;
	while(b){
		if(b&1) (res*=a)%=mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

int main(){
	// freopen("my.in","r",stdin);
	// freopen("my.out","w",stdout);
	ios::sync_with_stdio(false);cin.tie(0);
	// read(n);read(m);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>s;
		ac.Insert(s);
	}
	ac.GetFail();ac.DP();
	int ans=ac.GetAns();
	// printf("%d\n",((ksm(26,m,mod)-ans)%mod+mod)%mod);
	cout<<((ksm(26,m,mod)-ans)%mod+mod)%mod<<"\n";
	return 0;
}
/*
update 1:
row 48 "p[p[top].ch[i]].end|=p[p[top].fail].ch[i];"
update 2:
row 101 -> row 100
update 3
row 93 -> 92
*/

2.2 P4600 [HEOI2012]旅行问题

建立 AC 自动机,在 fail 树上求 LCA 就可以了。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 3000010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const ll mod=1e9+7;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

vector<int> ID[N];

struct node{
    int ch[26],fail,end;
    ll code;
};

struct edge{
    int to,next;
    inline void intt(int to_,int ne_){
        to=to_;next=ne_;
    }
};
edge li[N];
int head[N],tail;

inline void add(int from,int to){
    li[++tail].intt(to,head[from]);
    head[from]=tail;
}

struct AC{
    node p[N];int size;
    inline AC(){size=1;}
    inline void Insert(int id,char *s){
        int len=strlen(s);int now=1;
        for(int i=0;i<=len-1;i++){
            int k=s[i]-'a';
            if(!p[now].ch[k]) p[now].ch[k]=++size;
            int last=now;now=p[now].ch[k];
            p[now].code=(p[last].code*26%mod+k)%mod;
            ID[id].push_back(now);
        }
        p[now].end++;
    }
    inline void GetFail(){
        queue<int> q;
        for(int i=0;i<=25;i++){
            if(p[1].ch[i]){q.push(p[1].ch[i]);p[p[1].ch[i]].fail=1;}
            else p[1].ch[i]=1;
        }
        while(q.size()){
            int top=q.front();q.pop();
            for(int i=0;i<=25;i++){
                if(p[top].ch[i]){
                    p[p[top].ch[i]].fail=p[p[top].fail].ch[i];
                    q.push(p[top].ch[i]);
                }
                else p[top].ch[i]=p[p[top].fail].ch[i];
            }
        }
    }
    inline void BuildFailTree(){
        for(int i=2;i<=size;i++){
            // printf("i:%d fail:%d\n",i,p[i].fail);
            add(p[i].fail,i);
        }
    }
};
AC ac;

int n,q;
char s[N];

int top[N],son[N],siz[N],deep[N],fa[N];

inline void dfs1(int k,int fat){
    deep[k]=deep[fat]+1;siz[k]=1;fa[k]=fat;
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fat) continue;
        dfs1(to,k);
        siz[k]+=siz[to];
        if(siz[son[k]]<siz[to]) son[k]=to;
    }
}

inline void dfs2(int k,int t){
    top[k]=t;
    if(son[k]) dfs2(son[k],t);
    for(int x=head[k];x;x=li[x].next){
        int to=li[x].to;
        if(to==fa[k]||to==son[k]) continue;
        dfs2(to,to);
    }
}

inline int GetLca(int a,int b){
    while(top[a]!=top[b]){
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        a=fa[top[a]];
    }
    if(deep[a]>deep[b]) swap(a,b);
    return a;
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++){
        scanf("%s",s);ac.Insert(i,s);
    }
    ac.GetFail();ac.BuildFailTree();
    dfs1(1,0);dfs2(1,1);
    read(q);
    for(int i=1;i<=q;i++){
        int a,b,c,d;read(a);read(b);read(c);read(d);
        int p1=ID[a][b-1],p2=ID[c][d-1];
        // printf("p1:%d p2:%d\n",p1,p2);
        // printf("lca:%d\n",GetLca(p1,p2));
        printf("%lld\n",ac.p[GetLca(p1,p2)].code);
    }
    return 0;
}

3 KMP 算法

KMP 算法不会考匹配,而会考对 next 数组的理解和运用。

3.1 HDU3336

\(f_i\) 表示以 \(i\) 结尾的所有的前缀的出现次数,那么显而易见的转移是 \(f_i=f_{next_i}+1\) ,之所以加 \(1\) 是整个长度为 \(i\) 的字符串,所有以 \(next_i\) 结尾的前缀都会在以 \(i\) 结尾中出现 \(1\) 次。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const int mod=10007;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int t,n,nxt[N],f[N],ans;
char s[N];

int main(){
    read(t);
    while(t--){
        read(n);
        scanf("%s",s+1);
        nxt[1]=0;
        for(int i=2,j=0;i<=n;i++){
            while(j>0&&s[j+1]!=s[i]) j=nxt[j];
            if(s[j+1]==s[i]) j++;
            nxt[i]=j;
        }
        for(int i=1;i<=n;i++){
            f[i]=f[nxt[i]]+1;f[i]%=mod;
            ans+=f[i];ans%=mod;
        }
        printf("%d\n",ans);
        for(int i=1;i<=n;i++) nxt[i]=f[i]=0;ans=0;
    }
}

3.2 POJ 2752

KMP 的题我用字符串 hash 做的,就这样吧。。。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 400010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;
const ull mod=13331;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

ull a,b;
char s[N];

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    while(cin>>(s+1)){
        a=b=0;
        ull ji=1;int n=strlen(s+1);
        for(int i=1;i<=n;i++){
            a*=mod;a+=s[i]-'a';
            int j=n-i+1;int siz=n-j;
            b+=ji*(s[j]-'a');ji*=mod;
            if(a==b) printf("%d ",i);
        }
        putchar('\n');
    }
}

原文地址:https://www.cnblogs.com/TianMeng-hyl/p/15183862.html