poj 3415 后缀数组+单调栈||后缀自动机
来源:互联网 发布:美中国际 知乎 编辑:程序博客网 时间:2024/06/03 20:13
Common Substrings
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 11519 Accepted: 3814
Description
A substring of a string T is defined as:
T(i, k)=TiTi+1…Ti+k-1, 1≤i≤i+k-1≤|T|.
Given two strings A, B and one integer K, we define S, a set of triples (i, j, k):
S = {(i, j, k) | k≥K, A(i, k)=B(j, k)}.
You are to give the value of |S| for specific A, B and K.
Input
The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.
1 ≤ |A|, |B| ≤ 105
1 ≤ K ≤ min{|A|, |B|}
Characters of A and B are all Latin letters.
Output
For each case, output an integer |S|.
Sample Input
2
aababaa
abaabaa
1
xx
xx
0
Sample Output
22
5
问两个串大于等于k的长度的公共子串个数
后缀数组拼起来,
一个很明显的想法是一个块区间的首和尾的最大lcp是之间的最小值,所以对于每个属于B串的子串,我们要找出前面所有的A串和它的公共子串且大于等于k的个数,这样每个串的贡献应该是每个A串和B串和这个串公共子串,lcp-k+1,如何求每个A串和当前B串的lcp呢,暴力的话是on^2,不可行,由于开始我们把大于k的都放在一个块里面,所以在一个块里面,必然每个A都会对下面的B有贡献的,很明显我们是可以o1算出每个A串的贡献的 sum+=height数组-k+1,但是这是算到最大的贡献,很有可能当你遇到这个B的时候这两个公共lcp 比height数组小很多,所以使用单调栈,因为两者的lcp只会越来越小,把这些A串的height存在栈里面,如果这个height比当前的大,那么证明这个贡献必须减少,所以
while(tail>0&&st[tail]>height[i]) { sum-=(1ll*st[tail]-k+1)*cnt[tail]; sum+=(1ll*height[i]-k+1)*cnt[tail]; num+=cnt[tail]; tail--; }
我们同时用cnt数组记录这个串承载了多少了个串的本事,因为他们的贡献将变一样,所以就可以完全弹走了,而算A串的时候,B串没有初始化为num=1,所以必然不会有B串的贡献,很很很巧妙
#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int MAXN = 2e5+500; int t1[MAXN],t2[MAXN],c[MAXN]; int len1,len2;bool cmp(int *r,int a,int b,int l) { return r[a]==r[b]&&r[a+l]==r[b+l]; } void da(char str[],int sa[],int ra[],int height[],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]=str[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++) ra[sa[i]]=i; for(i=0;i<n;i++) { if(k) k--; j=sa[ra[i]-1]; while(str[i+k]==str[j+k])k++; height[ra[i]]=k; } } int ra[MAXN],height[MAXN]; int sa[MAXN],num[MAXN]; char str[MAXN];int st[MAXN];typedef long long ll;ll cnt[MAXN];ll solve(int k,int n){ int tail=0; ll sum=0,ans=0; for(int i=1;i<=n;i++) { if(height[i]<k) { sum=0; tail=0; continue; } ll num=0; while(tail>0&&st[tail]>height[i]) { sum-=(1ll*st[tail]-k+1)*cnt[tail]; sum+=(1ll*height[i]-k+1)*cnt[tail]; num+=cnt[tail]; tail--; } st[++tail]=height[i]; if(sa[i-1]<len1) { sum+=(1ll*height[i]-k+1); cnt[tail]=num+1; } else cnt[tail]=num; if(sa[i]>len1) ans+=sum; } tail=0; sum=0; for(int i=1;i<=n;i++) { if(height[i]<k) { sum=0; tail=0; continue; } ll num=0; while(tail>0&&st[tail]>height[i]) { sum-=(1ll*st[tail]-k+1)*cnt[tail]; sum+=(1ll*height[i]-k+1)*cnt[tail]; num+=cnt[tail]; tail--; } st[++tail]=height[i]; if(sa[i-1]>len1) { sum+=(1ll*height[i]-k+1); cnt[tail]=num+1; } else cnt[tail]=num; if(sa[i]<len1) ans+=sum; } return ans;}int main(){ int k; while(~scanf("%d",&k)) { if(!k) break; scanf("%s",str); len1=strlen(str); str[len1]='#'; scanf("%s",str+len1+1); len2=strlen(str); da(str,sa,ra,height,len2,130); printf("%lld\n",solve(k,len2) ); }}
Max 数组 能接受的字符串的最长长度
Min 数组 能接受的字符串的最小长度 和Max[fail[p]] 一样
right 能接受的字符串在原串中出现位置的集合
sz right的大小
Max-Min+1 就是每个状态的子串的数目
后缀自动机的做法就是,利用寻找最长公共子串这个匹配,遇到这个点,如果最大匹配长度大于等于k,那么
更新的是 ans+=Max[p]-max(Max[fail[i]]+1,k)+1)就好,然后更新父节点,首先匹配最长公共子串的时候能到这个点证明长度必然 >=Min[p] 那么父节点必然也可以更新,比较k和Min[p] 如果k大于等于Min[p],那么父节点必然没贡献,不然就是有贡献 标记一下,最后拓扑更新就行
#include <bits/stdc++.h>using namespace std;const int maxn = 200000+5;int last,tail,in[maxn],Min[maxn];int Max[maxn],cnt[maxn],vis[maxn];int nxt[maxn][26],fail[maxn];char sa[maxn],sb[maxn];typedef long long ll;ll sz[maxn];int Maxi[maxn];inline void build(char *s){ while(*s) { int p=last,t=++tail,c=*s++-'a'; Max[t]=Max[p]+1; sz[t]=1; Maxi[t]=0; while(p&&!nxt[p][c]) nxt[p][c]=t,p=fail[p]; if(p) { int q=nxt[p][c]; if(Max[q]==Max[p]+1) fail[t]=q,Min[t]=Max[q]+1; else { int k=++tail; Maxi[k]=0; fail[k]=fail[q]; fail[t]=fail[q]=k; Max[k]=Max[p]+1; memcpy(nxt[k],nxt[q],sizeof(nxt[q])); while(p&&nxt[p][c]==q) nxt[p][c]=k,p=fail[p]; } } else fail[t]=Min[t]=1; last=t; }}int b[maxn];ll flag[maxn];int main(){ int k; while(~scanf("%d",&k)) { if(!k) break; int n; memset(nxt,0,sizeof(nxt)); memset(b,0,sizeof(b)); memset(cnt,0,sizeof(cnt)); memset(flag,0,sizeof(flag)); scanf(" %s",sa); last=1,tail=1; build(sa); int hh=strlen(sa); for(int i=1;i<=tail;i++) cnt[Max[i]]++; for(int i=1;i<=hh;i++) cnt[i]+=cnt[i-1]; for(int i=1;i<=tail;i++) b[cnt[Max[i]]--]=i; for(int i=tail;i>=1;i--) sz[fail[b[i]]]+=sz[b[i]]; ll ans=0; scanf(" %s",sb); n=strlen(sb); int p=1; int len=0; for(int i=0;i<n;i++) { int c=sb[i]-'a'; if(nxt[p][c]) len++,p=nxt[p][c]; else { while(p&&!nxt[p][c]) p=fail[p]; if(!p) p=1,len=0; else { len=Max[p]+1; p=nxt[p][c]; } }// Maxi[p]=max(Maxi[p],len);// if(len>=k) flag[p]++; if(len>=k) { ans+=1ll*(len-max(k,Max[fail[p]]+1)+1)*sz[p]; if(k<=Max[fail[p]]) flag[fail[p]]++; } } for(int i=tail;i>=1;i--) { int p=b[i]; ans+=max(0ll,1ll*flag[p]*(Max[p]-max(Max[fail[p]]+1,k)+1)*sz[p]); if(k<=Max[fail[p]]) flag[fail[p]]+=flag[p]; } printf("%lld\n",ans ); }}
- poj 3415 后缀数组+单调栈||后缀自动机
- POJ 3415 后缀数组 + 单调栈
- poj 3415 后缀数组+单调队列
- poj 3415 Common Substrings(后缀数组 | 后缀自动机)
- POJ 3415 Common Substrings(后缀数组+单调栈)
- POJ 3415 Common Substrings 后缀数组 计数 单调栈优化
- 【POJ】3415 Common Substrings 【后缀数组+单调栈】
- POJ 3415 Common Substrings 后缀数组 + 单调栈维护
- poj 3415 Common Substrings(后缀数组+单调栈)
- POJ 3415:Common Substrings 后缀数组+单调栈
- POJ 3415 Common Substrings(后缀数组+单调栈)
- [POJ 3415] Common Substrings (后缀数组+单调栈优化)
- poj 3415 Common Substrings (后缀数组+单调栈)
- POJ - 3415 Common Substrings 后缀数组+单调栈+前缀和
- POJ 3415 Common Substrings <后缀数组+单调栈>
- POJ-3415-Common Substrings(后缀数组+单调栈)
- poj 3415 Common Substrings(后缀数组+单调栈+dp)
- [BZOJ3238][Ahoi2013]差异(后缀数组+单调栈||后缀自动机+树形dp)
- LwIP裸机ping问题
- Maven下载、安装和配置(一)
- 以Point类为基础,定义一个平面中的Circle类: 1、编写一个无参的构造函数; 2、编写一个有参的构造函数; 3、在主函数中调用无参的构造函数生成圆的实例c1,调用有参的构造函数生成圆的实例c2
- qnx的ssh功能配置,nfs配置,scp配置,telnet配置
- c++ 左值、右值、左值引用和右值引用
- poj 3415 后缀数组+单调栈||后缀自动机
- HID Firmware修改 (HIDtoUARTExample)之二
- angular $http post 方法有些浏览器不支持原因解答
- POJ
- Linux部署Oracle数据库图形界面乱码
- js的promise应用
- HTML跑马灯
- Vue 组件实现表单的双向绑定
- hdu 1317 XYZZY(floyd+SPFA)