字符串算法学习笔记
天坑,大概暑假就能填完了。
符号使用说民
一般用\(S,T\)表示一个字符串
字符串一般从1开始
用\(S[l\ldots r]\)表示一个子串
用\(S\in T\)表示\(S\)是\(T\)的子串
题目后的\(\text{easy/normal/hard/lunatic}\)表示题目的难度(大概对应NOIp d1t1+/NOIp d1d2+/弱省省选/强省省选)
kmp学习笔记
exkmp
咕了(
AC自动机总结
假设你们都懂AC自动机是啥了
fail树
\(\text{fail}\)树就是连接所有\(fail_u\)和\(u\)的树。
考虑到\(fail_u\)所代表的串\(\in u\)所代表的串
所以若u所代表的串\(\in S\),则在fail树上u到根的所有节点代表的串也\(\in S\)
我们来看几个例题学习一下
P3966 [TJOI2013]单词 (normal)
将fail树建出来。
然后将每一个串所走过的位置+1
然后每一个串的出现次数就是结束位置的fail树中的子树的权值之和。
代码如下
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int > vi; typedef pair<int ,int > pii; const int INF=0x3f3f3f3f, maxn=1000007; const ll MOD=1004535809; const ll LINF=0x3f3f3f3f3f3f3f3fLL; const ll P=19260817; int n; char s[maxn]; int tr[maxn][26]; int fail[maxn]; int num[maxn]; int tot=1; int sum[maxn]; void ins(char *s,int id){ int len=strlen(s); int u=1; for(int i=0;i<len;i++){ int c=s[i]-'a'; if(!tr[u][c])tr[u][c]=++tot; u=tr[u][c]; sum[u]++; } num[id]=u; } struct edge{ int to,nxt; }e[maxn<<1]; int head[maxn],tot_e; void addedge(int u,int v){ e[++tot_e]=(edge){v,head[u]}; head[u]=tot_e; } queue<int > q; void build(){ for(int i=0;i<26;i++)tr[0][i]=1; q.push(1); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]); else tr[u][i]=tr[fail[u]][i]; } } for(int i=2;i<=tot;i++){ addedge(fail[i],i); } } void dfs(int u,int f){ for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs(v,u); sum[u]+=sum[v]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); ins(s,i); } build(); dfs(1,0); for(int i=1;i<=n;i++)printf("%d\n",sum[num[i]]); return 0; }
P2414 [NOI2011]阿狸的打字机(hard)
将所有查询离线并建一个ac自动机。
这个串的ac自动机事实上很好建。
遇到P就标记一下,遇到B就跳到他父亲。
我们考虑一组查询(x,y)表示x在y中出现了多少次
相当与在trie中把y到根的所有节点+1,然后查询在fail树中的子树之和。
所以我们dfs一遍trie树,如果我们遍历到一个串y,我们就查询所有它所有串。
查询子树和我们就对在fail树中dfs序建一棵BIT就行了.
写起来细节有点多
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define fi first #define se second #define y0 pmt #define y1 pmtpmt #define x0 pmtQAQ #define x1 pmtQwQ // #define getchar nc using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int > vi; typedef pair<int ,int > pii; const int INF=0x3f3f3f3f, maxn=200007; const ll MOD=1004535809; const ll LINF=0x3f3f3f3f3f3f3f3fLL; const ll P=19260817; char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar(); return x*f; } void write(int x){ if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[20];register int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } int tr[maxn][26]; int fail[maxn]; int f[maxn]; int cnt=1,id_n=0; int num[maxn]; int tag[maxn]; int vis[maxn][26]; void ins(char *s){ int u=1,len=strlen(s); for(int i=0;i<len;i++){ if(s[i]=='P'){ num[++id_n]=u; tag[u]=1; }else if(s[i]=='B'){ u=f[u]; }else { int c=s[i]-'a'; if(!tr[u][c]){tr[u][c]=++cnt;f[tr[u][c]]=u;} u=tr[u][c]; } } } struct edge{ int to,nxt; }e[maxn<<1]; int head[maxn],tot_e; void addedge(int u,int v){ e[++tot_e]=(edge){v,head[u]}; head[u]=tot_e; } queue<int > q; void build(){ for(int i=0;i<26;i++)tr[0][i]=1; q.push(1); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]); else tr[u][i]=tr[fail[u]][i]; } } for(int i=2;i<=cnt;i++){ addedge(fail[i],i); } } int dfn_cnt; int dfn[maxn]; int sz[maxn]; void dfs1(int u,int f){ dfn[u]=++dfn_cnt; sz[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs1(v,u); sz[u]+=sz[v]; } } struct BIT{ int t[maxn]; #define N 200000 void update(int x,int val){x++;for(int i=x;i<=N;i+=(i&-i))t[i]+=val;} int query(int x){x++;int rt=0;for(int i=x;i;i-=(i&(-i)))rt+=t[i];return rt;} }T; vector<pii > query[maxn]; int ans[maxn]; void dfs2(int u){ // cout<<u<<endl; T.update(dfn[u],1); if(tag[u]){ // cout<<u<<' '<<query[u].size()<<endl; for(int i=0;i<query[u].size();i++){ int v=query[u][i].fi; ans[query[u][i].se]=T.query(dfn[v]+sz[v]-1)-T.query(dfn[v]-1); } } // cout<<u<<' '<<tag[u]<<endl; for(int i=0;i<26;i++){ if(tr[u][i]&&vis[u][i])dfs2(tr[u][i]); } T.update(dfn[u],-1); } char s[maxn]; int n; int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); scanf("%s",s); ins(s); build(); dfs1(1,0); scanf("%d",&n); for(int i=1,x,y;i<=n;i++){ scanf("%d%d",&x,&y); query[num[y]].pb(mp(num[x],i)); } dfs2(1); for(int i=1;i<=n;i++)printf("%d\n",ans[i]); return 0; }
CF1207G Indie Album
和上一题差不多吧……
后面补SAM做法……。
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define fi first #define se second #define y0 pmt #define y1 pmtpmt #define x0 pmtQAQ #define x1 pmtQwQ // #define getchar nc using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int > vi; typedef pair<int ,int > pii; const int INF=0x3f3f3f3f, maxn=800007; const ll MOD=1004535809; const ll LINF=0x3f3f3f3f3f3f3f3fLL; const ll P=19260817; char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar(); return x*f; } void write(int x){ if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[20];register int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } int n,m; char s[maxn]; vector<pair<int ,int > > t[maxn]; void ins1(){ for(int i=1;i<=n;i++){ int op; char c[2]; scanf("%d",&op); if(op==1){ scanf("%s",c); t[1].pb(mp(c[0]-'a',i+1)); }else { int j; scanf("%d%s",&j,c); t[j+1].pb(mp(c[0]-'a',i+1)); } } } int tr[maxn][26]; int fail[maxn]; int tot_cnt=1; int num[maxn]; vector<pii > vec[maxn]; void ins(char *s,int id){ int u=1,len=strlen(s); for(int i=0;i<len;i++){ int c=s[i]-'a'; if(!tr[u][c])tr[u][c]=++tot_cnt; u=tr[u][c]; } num[id]=u; } struct edge{ int to,nxt; }e[maxn<<1]; int head[maxn],tot_e; void addedge(int u,int v){ e[++tot_e]=(edge){v,head[u]}; head[u]=tot_e; } queue<int > q; void build(){ for(int i=0;i<26;i++)tr[0][i]=1; q.push(1); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]); else tr[u][i]=tr[fail[u]][i]; } } for(int i=2;i<=tot_cnt;i++){ addedge(fail[i],i); } } int dfn_cnt; int dfn[maxn]; int sz[maxn]; void dfs1(int u,int f){ dfn[u]=++dfn_cnt; sz[u]=1; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if(v==f)continue; dfs1(v,u); sz[u]+=sz[v]; } } struct BIT{ int t[maxn]; #define N 500000 void update(int x,int val){x++;for(int i=x;i<=N;i+=(i&-i))t[i]+=val;} int query(int x){x++;int rt=0;for(int i=x;i;i-=(i&(-i)))rt+=t[i];return rt;} }T; int ans[maxn]; void dfs2(int u1,int u2){ T.update(dfn[u2],1); for(int i=0;i<vec[u1].size();i++){ int v=vec[u1][i].fi; ans[vec[u1][i].se]=T.query(dfn[v]+sz[v]-1)-T.query(dfn[v]-1); } for(int i=0;i<t[u1].size();i++){ int c=t[u1][i].fi; int v=t[u1][i].se; dfs2(v,tr[u2][c]); } T.update(dfn[u2],-1); } int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); scanf("%d",&n); ins1(); scanf("%d",&m); for(int i=1;i<=m;i++){ int x; scanf("%d%s",&x,s); ins(s,i); vec[x+1].pb(mp(num[i],i)); } build(); dfs1(1,0); dfs2(1,1); for(int i=1;i<=m;i++){ printf("%d\n",ans[i]); } return 0; }
AC自动机上DP
P4052 [JSOI2007]文本生成器
设\(dp[i][j]\)表示构造一个长为i,匹配到j节点的串的不合法(即没有合法子串)方案数。
转移显然
最后拿\(26^m\)减一下就好了。
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define fi first #define se second #define y0 pmt #define y1 pmtpmt #define x0 pmtQAQ #define x1 pmtQwQ // #define getchar nc using namespace std; typedef long long ll; typedef unsigned long long ull; typedef vector<int > vi; typedef pair<int ,int > pii; const int INF=0x3f3f3f3f, maxn=20007; const ll MOD=10007; const ll LINF=0x3f3f3f3f3f3f3f3fLL; const ll P=19260817; char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=((x<<3)+(x<<1)+ch-'0')%MOD,ch=getchar(); return x*f; } void write(int x){ if(!x)putchar('0');if(x<0)x=-x,putchar('-'); static int sta[20];register int tot=0; while(x)sta[tot++]=x%10,x/=10; while(tot)putchar(sta[--tot]+48); } int tr[maxn][26]; int fail[maxn]; int dng[maxn]; int tot=1; void ins(char *s){ int u=1,len=strlen(s); for(int i=0;i<len;i++){ int c=s[i]-'A'; if(!tr[u][c])tr[u][c]=++tot; u=tr[u][c]; } dng[u]=1; } struct edge{ int to,nxt; }e[maxn<<1]; int head[maxn],tot_e; void addedge(int u,int v){ e[++tot_e]=(edge){v,head[u]}; head[u]=tot_e; } queue<int > q; void build(){ for(int i=0;i<26;i++)tr[0][i]=1; q.push(1); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=0;i<26;i++){ if(tr[u][i])fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]); else tr[u][i]=tr[fail[u]][i]; } } for(int i=2;i<=tot;i++){ // addedge(u,fail[u]); addedge(fail[i],i); } } void dfs(int u,int flag){ dng[u]|=flag; for(int i=head[u];i;i=e[i].nxt){ int v=e[i].to; dfs(v,dng[u]); } } ll qpow(ll a,ll b){ ll rt=1; while(b){ if(b&1)rt=rt*a%MOD; a=a*a%MOD; b>>=1; } return rt; } int n,m; char s[maxn]; int dp[107][maxn]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%s",s); ins(s); } build(); dfs(1,0); dp[0][1]=1; for(int i=1;i<=m;i++){ for(int j=1;j<=tot;j++){ for(int c=0;c<26;c++){ if(!dng[tr[j][c]])dp[i][tr[j][c]]=(dp[i][tr[j][c]]+dp[i-1][j])%MOD; } } } ll ans=qpow(26,m); for(int i=1;i<=tot;i++){ ans=(ans-dp[m][i]+MOD)%MOD; } printf("%lld",ans); return 0; }
杂题
后缀数据结构学习
来源:https://www.cnblogs.com/pmt2018/p/12234502.html