后缀自动机的一些简单题目
来源:互联网 发布:天猫抢购软件 编辑:程序博客网 时间:2024/05/16 06:42
对于后缀自动机的论文很多,这里记录一些比较简单的题目便于熟悉后缀自动机的基本应用只有四道题目有一点少,今后会好好填坑
BZOJ4032: [HEOI2015]最短不公共子串
一句话题意:给两个串 A 和 B,求 A 最短子串且不是 B 的子串,A 最短子串且不是B的子序列,A 最短子序列且不是 B 的子串,A 最短子序列且不是 B 的子序列
做法:建出后缀自动机和序列自动机,自动机上每一条路径都相当于一个子串(子序列),从根节点 bfs 一遍,找到第一个 A 有的而 B 没有的点,就是字典序最小的
#include<cstring>#include<iostream>#include<algorithm>#include<cstdio>#include<queue>#define rep(i,l,r) for (int i=l;i<=r;i++)#define down(i,l,r) for (int i=l;i>=r;i--)#define clr(x,y) memset(x,y,sizeof(x))#define maxn 4005#define ll long long#define inf int(1e9)using namespace std;char sa[maxn],sb[maxn];int f[maxn][maxn];int n,m;int read(){ int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} return x*f;}struct data{ struct sam{int l,fa,ch[30];} sam[maxn]; int tot,root,last,head[30]; void init(){ clr(sam,0); tot=1; clr(head,0); } void extend(int c){ int p,np,q,nq; p=last,np=++tot; last=np; sam[np].l=sam[p].l+1; for (;p&&!sam[p].ch[c];p=sam[p].fa) sam[p].ch[c]=np; if (!p) sam[np].fa=1; else { q=sam[p].ch[c]; if (sam[p].l+1==sam[q].l) sam[np].fa=q; else { nq=++tot; sam[nq].l=sam[p].l+1; memcpy(sam[nq].ch,sam[q].ch,sizeof(sam[q].ch)); sam[nq].fa=sam[q].fa; sam[np].fa=sam[q].fa=nq; for (;sam[p].ch[c]==q;p=sam[p].fa) sam[p].ch[c]=nq; } } } void sambuild(int n,char s[]){ init(); tot=last=1; rep(i,1,n) extend(s[i]-'a'); } void quebuild(int n,char s[]){ int o,p,c; init(); rep(i,0,25) head[i]=1; rep(i,1,n){ o=++tot; c=s[i]-'a'; rep(j,0,25) for (p=head[j];p&&!sam[p].ch[c];p=sam[p].fa) sam[p].ch[c]=o; sam[o].fa=head[c]; head[c]=o; } }} A,B;struct node{int x,y;};int solve(){ queue<node> q; clr(f,0); q.push((node){1,1}); f[1][1]=0; while (!q.empty()){ int ux=q.front().x,uy=q.front().y; q.pop(); rep(i,0,25){ if (!A.sam[ux].ch[i]) continue; if (!B.sam[uy].ch[i]) return f[ux][uy]+1; int vx=A.sam[ux].ch[i],vy=B.sam[uy].ch[i]; if (!f[vx][vy]) q.push((node){vx,vy}),f[vx][vy]=f[ux][uy]+1; } } return -1;}int main(){ scanf("%s",sa+1); scanf("%s",sb+1); n=strlen(sa+1); m=strlen(sb+1); A.sambuild(n,sa); B.sambuild(m,sb); printf("%d\n",solve()); B.quebuild(m,sb); printf("%d\n",solve()); A.quebuild(n,sa); B.sambuild(m,sb); printf("%d\n",solve()); B.quebuild(m,sb); printf("%d\n",solve()); return 0;}
【弱省胡策】Round #0 Luv Letter
一句话题意:给串 A 和 B ,求是 A 的子串(子序列)且不是 B 的子串(子序列)的数目
做法:对 A B 都建出自动机后 dfs 一遍将节点的贡献加在其父亲上就可以知道每个点的子树大小,这样只要扫一遍找出 A 有的而 B 没有的节点,将其子树大小加到答案中就可以了
#include<cstring>#include<iostream>#include<algorithm>#include<cstdio>#include<queue>#define rep(i,l,r) for (int i=l;i<=r;i++)#define down(i,l,r) for (int i=l;i>=r;i--)#define clr(x,y) memset(x,y,sizeof(x))#define maxn 4005#define ll long long#define inf int(1e9)#define mm 1000000007using namespace std;char sa[maxn],sb[maxn];ll f[maxn][maxn];int vis[maxn][maxn];int n,m;int read(){ int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} return x*f;}struct data{ struct sam{int l,fa,ch[30];ll sz;} sam[maxn]; int tot,root,last,head[30]; void init(){ clr(sam,0); tot=1; clr(head,0); } void extend(int c){ int p,np,q,nq; p=last,np=++tot; last=np; sam[np].l=sam[p].l+1; for (;p&&!sam[p].ch[c];p=sam[p].fa) sam[p].ch[c]=np; if (!p) sam[np].fa=1; else { q=sam[p].ch[c]; if (sam[p].l+1==sam[q].l) sam[np].fa=q; else { nq=++tot; sam[nq].l=sam[p].l+1; memcpy(sam[nq].ch,sam[q].ch,sizeof(sam[q].ch)); sam[nq].fa=sam[q].fa; sam[np].fa=sam[q].fa=nq; for (;sam[p].ch[c]==q;p=sam[p].fa) sam[p].ch[c]=nq; } } } void dfs(int u){ if (!u||sam[u].sz) return; sam[u].sz=1; rep(i,0,25) { int v=sam[u].ch[i]; if (!v) continue; if (!sam[v].sz) dfs(v); sam[u].sz=(sam[u].sz+sam[v].sz)%mm; } } void sambuild(int n,char s[]){ init(); tot=last=1; rep(i,1,n) extend(s[i]-'a'); dfs(1); } void quebuild(int n,char s[]){ int o,p,c; init(); rep(i,0,25) head[i]=1; rep(i,1,n){ o=++tot; c=s[i]-'a'; rep(j,0,25) for (p=head[j];p&&!sam[p].ch[c];p=sam[p].fa) sam[p].ch[c]=o; sam[o].fa=head[c]; head[c]=o; } dfs(1); }} A,B;struct node{int x,y;};ll dfs(int x,int y){ if (vis[x][y]) return f[x][y]; vis[x][y]=1; if (!y) return f[x][y]=A.sam[x].sz; f[x][y]=0; rep(i,0,25){ int vx=A.sam[x].ch[i],vy=B.sam[y].ch[i]; if (!vx) continue; f[x][y]=(f[x][y]+dfs(vx,vy))%mm; } return f[x][y]=f[x][y];}ll solve(){ clr(vis,0); return dfs(1,1);}int main(){ scanf("%s",sa+1); scanf("%s",sb+1); n=strlen(sa+1); m=strlen(sb+1); A.sambuild(n,sa); B.sambuild(m,sb); printf("%lld\n",solve()); B.quebuild(m,sb); printf("%lld\n",solve()); A.quebuild(n,sa); B.sambuild(m,sb); printf("%lld\n",solve()); B.quebuild(m,sb); printf("%lld\n",solve()); return 0;}
BZOJ3998: [TJOI2015]弦论
一句话题意:求一个子串的第 k 小子串 (分多个相同子串算一个和算多个两种情况)
做法:如果是多个算多个的话那么每个点的贡献要加到其fail上去否则就不用,然后算出自动机上每个点的子树大小,顺序遍历一遍就可以了
代码实现:因为 SAM 中的节点的 fail 的编号可能在后面,我写的做法是将长度进行桶排,从长度大的算到长度小的。当然还有一种做法是建出 fail 树将子节点的贡献加到父亲上去
#include<cstring>#include<cstdio>#include<iostream>#include<algorithm>#include<cmath>#define rep(i,l,r) for (int i=l;i<=r;i++)#define down(i,l,r) for (int i=l;i>=r;i--)#define clr(x,y) memset(x,y,sizeof(x))#define maxn 1000500using namespace std;int last,tot,n,T,K;char s[maxn];int fa[maxn],ch[maxn][31],l[maxn],q[maxn],b[maxn],sum[maxn],val[maxn],vis[maxn];int read(){ int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} return x*f;}void expand(int c){ int p,np,q,nq; p=last; last=np=++tot; l[np]=l[p]+1; for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np; if (!p) fa[np]=1; else { q=ch[p][c]; if (l[q]==l[p]+1) fa[np]=q; else { nq=++tot; l[nq]=l[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[nq])); fa[nq]=fa[q]; fa[np]=fa[q]=nq; for (;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } val[np]=1;}void dfs(int u,int k){ if (k>=sum[u]) return; k-=val[u]; if (!k) return; rep(j,0,25) if (ch[u][j]){ int v=ch[u][j]; if (sum[v]>=k){ putchar(j+'a'); dfs(v,k); return ; } k-=sum[v]; }}int main(){ scanf("%s",s+1); n=strlen(s+1); tot=last=1; rep(i,1,n) expand(s[i]-'a'); T=read(); K=read(); rep(i,1,tot) b[l[i]]++; rep(i,1,n) b[i]+=b[i-1]; rep(i,1,tot) q[b[l[i]]--]=i; down(i,tot,1) { int t=q[i]; if (T==1) val[fa[t]]+=val[t]; else val[t]=1; } val[1]=0; down(i,tot,1){ int t=q[i]; sum[t]=val[t]; rep(j,0,25) sum[t]+=sum[ch[t][j]]; } if (sum[1]<K) {puts("-1"); return 0;} dfs(1,K); return 0;}
BZOJ3238: [Ahoi2013]差异
一句话题意:求一个串的两两后缀的 lcp
做法:逆序建出 SAM,它的 fail 树就是后缀树,然后扫一遍就可以了
#include<cstring>#include<iostream>#include<algorithm>#include<cstdio>#include<queue>#define rep(i,l,r) for (int i=l;i<=r;i++)#define down(i,l,r) for (int i=l;i>=r;i--)#define clr(x,y) memset(x,y,sizeof(x))#define maxn 1005000#define ll long long#define inf int(1e9)#define mm 1000000007using namespace std;char s[maxn];ll ans;int n,m;int read(){ int x=0,f=1; char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();} while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} return x*f;}struct data{ struct sam{int l,fa,ch[30];ll sz;} sam[maxn]; int tot,root,last; void init(){ clr(sam,0); tot=1; } void extend(int c){ int p,np,q,nq; p=last,np=++tot; last=np; sam[np].l=sam[p].l+1; for (;p&&!sam[p].ch[c];p=sam[p].fa) sam[p].ch[c]=np; if (!p) sam[np].fa=1; else { q=sam[p].ch[c]; if (sam[p].l+1==sam[q].l) sam[np].fa=q; else { nq=++tot; sam[nq].l=sam[p].l+1; memcpy(sam[nq].ch,sam[q].ch,sizeof(sam[q].ch)); sam[nq].fa=sam[q].fa; sam[np].fa=sam[q].fa=nq; for (;sam[p].ch[c]==q;p=sam[p].fa) sam[p].ch[c]=nq; } } sam[np].sz=1; } void sambuild(int n,char s[]){ tot=last=1; down(i,n,1) extend(s[i]-'a'); }} A;ll sum[maxn];int l[maxn],head[maxn],tot;struct node{int obj,pre;}e[maxn];void insert(int x,int y){ e[++tot].obj=y; e[tot].pre=head[x]; head[x]=tot;}void dfs(int u){ for (int j=head[u];j;j=e[j].pre){ int v=e[j].obj; dfs(v); ans-=2LL*l[u]*sum[u]*sum[v]; sum[u]+=sum[v]; }}int main(){ scanf("%s",s+1); n=strlen(s+1); ans=1LL*n*(n-1)*(n+1)/2; A.sambuild(n,s); rep(i,1,A.tot) l[i]=A.sam[i].l,sum[i]=A.sam[i].sz,insert(A.sam[i].fa,i); dfs(1); printf("%lld\n",ans); return 0;}
阅读全文
0 0
- 后缀自动机的一些简单题目
- 关于后缀自动机的一点题目
- 后缀自动机题目小结
- AC自动机的一些题目及思路
- 后缀自动机的构造
- 后缀自动机的构造
- SPOJ 题目7528 SUBLEX - Lexicographical Substring Search(后缀自动机求排名k的子串)
- 【后缀自动机-后缀树上的维护】hdu4641
- 后缀排序 后缀自动机的应用
- 后缀自动机的自我消化
- 后缀自动机的程序实现
- 后缀自动机实现的例子
- 后缀自动机的直观理解
- 关于后缀自动机的构造
- 后缀自动机
- 后缀自动机
- 后缀自动机
- 后缀自动机
- hdu 6208 hash字符串水题
- 《统计学习方法》-第二章-感知机
- Servlet的三种开发方法
- selenium之开发登录脚本_maven版本二(加入Properties)
- Git 连接 GitHub
- 后缀自动机的一些简单题目
- for 语句初始化后第二步是条件判断
- 自定义标题栏(左中右三块)
- LoRa芯片的八种工作模式解析
- ssm整合
- python 全局变量引用与修改
- 十一个行为型模式10:模板方法模式-Template Method Pattern【学习难度:★★☆☆☆,使用频率:★★★☆☆】
- 29、数据结构笔记之二十九数组之硬币抛掷模拟
- 用pyinstaller生成exe遇到的问题