poj3415 Common Substrings(后缀数组,单调栈 | 后缀自动机)
【题目链接】
http://poj.org/problem?id=3415
【题意】
A与B长度至少为k的公共子串个数。
【思路】
基本思想是将AB各个后缀的lcp-k+1的值求和。首先将两个字符串拼接起来中间用未出现的字符隔开,划分height数组,这首先保证了每一组中字符串之间的公共子串至少有k长度,组与组之间互不干扰。
问题变成了求一个组中一个A串与之前B串形成的LCP(lcp-k+1)和一个B串与之前A串形成的LCP,问题是对称的,这里先解决第一个。用一个单调栈,栈中存放两个元素分别height_top与cnt_top,分别表示到i为止的最小height和A串的数目。维护栈中元素的height从顶到底递减:每加入一个元素如果该元素比栈顶元素小则需要将tot中cnt_top个已经累计的height_top全部替换为当前元素的height(lcp是取区间最小值)。
时间复杂度为O(n)。
【代码】
#include<cstdio>
#include<cstring>
#include<iostream>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; typedef long long LL;
const int maxn = + ; int s[maxn];
int sa[maxn],c[maxn],t[maxn],t2[maxn]; void build_sa(int m,int n) {
int i,*x=t,*y=t2;
for(i=;i<m;i++) c[i]=;
for(i=;i<n;i++) c[x[i]=s[i]]++;
for(i=;i<m;i++) c[i]+=c[i-];
for(i=n-;i>=;i--) sa[--c[x[i]]]=i;
for(int k=;k<=n;k<<=) {
int p=;
for(i=n-k;i<n;i++) y[p++]=i;
for(i=;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
for(i=;i<m;i++) c[i]=;
for(i=;i<n;i++) c[x[y[i]]]++;
for(i=;i<m;i++) c[i]+=c[i-];
for(i=n-;i>=;i--) sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=; x[sa[]]=;
for(i=;i<n;i++)
x[sa[i]]=y[sa[i]]==y[sa[i-]] && y[sa[i]+k]==y[sa[i-]+k]?p-:p++;
if(p>=n) break;
m=p;
}
}
int rank[maxn],height[maxn];
void getHeight(int n) {
int i,j,k=;
for(i=;i<=n;i++) rank[sa[i]]=i;
for(i=;i<n;i++) {
if(k) k--;
j=sa[rank[i]-];
while(s[j+k]==s[i+k]) k++;
height[rank[i]]=k;
}
} int n,k;
char a[maxn],b[maxn];
int sta[maxn][]; int main() {
while(scanf("%d",&k)== && k) {
scanf("%s%s",a,b);
int lena=strlen(a),lenb=strlen(b);
n=;
for(int i=;i<lena;i++) s[n++]=a[i];
s[n++]=;
for(int i=;i<lenb;i++) s[n++]=b[i];
s[n]=; build_sa('z'+,n+);
getHeight(n); int top=;
LL ans=,tot=;
for(int i=;i<=n;i++) {
if(height[i]<k) top=,tot=;
else {
int cnt=;
if(sa[i-]<lena) {
cnt++; tot+=height[i]-k+;
}
while(top && height[i]<=sta[top-][]) {
top--;
tot+=(height[i]-sta[top][])*sta[top][];
cnt+=sta[top][];
}
sta[top][]=height[i],sta[top++][]=cnt;
if(sa[i]>lena) ans+=tot;
}
}
top=tot=;
for(int i=;i<=n;i++) {
if(height[i]<k) top=,tot=;
else {
int cnt=;
if(sa[i-]>lena) {
cnt++; tot+=height[i]-k+;
}
while(top && height[i]<=sta[top-][]) {
top--;
tot+=(height[i]-sta[top][])*sta[top][];
cnt+=sta[top][];
}
sta[top][]=height[i],sta[top++][]=cnt;
if(sa[i]<lena) ans+=tot;
}
}
cout<<ans<<"\n";
}
return ;
}
UPD.16/4/6
【思路】
用字符串A构造SAM,在SAM上匹配第二个字符串B,设当前匹配长度为len,且位于状态p,则当前状态中满足条件长度不小于K的公共子串的字符串个数为
sum = len-max{ K,Min(p) }+1
SAM中一个状态代表的字符串长度为一个连续区间[ Min(s),Max(s) ],Min(s)为最小长度。
这些字符串重复的次数为|right|,即right集的大小,可以递推得到,则当前状态对于答案的贡献为sum*|right|
这时候匹配的是p,还应该统计parent树中p->root的路径上的状态中满足条件的个数。
这里设一个懒标记tag[x],记录节点x需要统计的次数,最后算一遍,每次如果Max(p->fa) >= K则上传标记。
需要注意的是可能出现大写字符 =_=
相比较而言SAM的做法更好想一些。
【代码】
#include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
#define rep(a,b,c) for(int a=(b);a>=(c);a--)
using namespace std; typedef long long ll;
const int N = 2e5+; int K;
char A[N],B[N]; int get(char c)
{
if(c>='a'&&c<='z') return c-'a';
else return c-'A'+;
} struct SAM
{
int sz,last;
int ch[N][],fa[N],l[N],c[N],b[N],tag[N];
int siz[N]; ll ans; void init() {
sz=; last=++sz;
memset(l,,sizeof(l));
memset(siz,,sizeof(siz));
memset(fa,,sizeof(fa));
memset(ch,,sizeof(ch));
memset(c,,sizeof(c));
memset(tag,,sizeof(tag));
}
void Add(int c) {
int np=++sz,p=last; last=np;
l[np]=l[p]+; siz[np]=;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=;
else {
int q=ch[p][c];
if(l[q]==l[p]+) fa[np]=q;
else {
int nq=++sz; l[nq]=l[p]+;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];
fa[np]=fa[q]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void get_right() {
FOR(i,,sz) c[l[i]]++;
FOR(i,,last) c[i]+=c[i-];
FOR(i,,sz) b[c[l[i]]--]=i;
rep(i,sz,) siz[fa[b[i]]]+=siz[b[i]];
}
ll solve(char* s) {
int len=,p=;
ans=;
for(int i=;s[i];i++) {
int c=get(s[i]);
if(ch[p][c]) {
len++; p=ch[p][c];
} else {
while(p&&!ch[p][c]) p=fa[p];
if(!p) {
p=; len=;
} else {
len=l[p]+; p=ch[p][c];
}
}
if(len>=K) {
ans+=(ll)(len-max(K,l[fa[p]]+)+)*siz[p];
if(l[fa[p]]>=K) tag[fa[p]]++;
}
}
rep(j,sz,) {
int i=b[j];
ans+=(ll)tag[i]*(l[i]-max(K,l[fa[i]]+)+)*siz[i];
if(l[fa[i]]>=K) tag[fa[i]]+=tag[i];
}
return ans;
} }sam; int main()
{
while(scanf("%d",&K)== && K) {
scanf("%s%s",A,B);
sam.init();
for(int i=;A[i];i++)
sam.Add(get(A[i]));
sam.get_right();
printf("%lld\n",sam.solve(B));
}
return ;
}
poj3415 Common Substrings(后缀数组,单调栈 | 后缀自动机)的更多相关文章
- 【BZOJ-3238】差异 后缀数组 + 单调栈
3238: [Ahoi2013]差异 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1561 Solved: 734[Submit][Status] ...
- BZOJ_3879_SvT_后缀数组+单调栈
BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...
- BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈
BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao ...
- BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)
BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...
- 【BZOJ3879】SvT 后缀数组+单调栈
[BZOJ3879]SvT Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干 ...
- BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】
题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...
- POJ3415 Common Substrings —— 后缀数组 + 单调栈 公共子串个数
题目链接:https://vjudge.net/problem/POJ-3415 Common Substrings Time Limit: 5000MS Memory Limit: 65536K ...
- poj 3415 Common Substrings(后缀数组+单调栈)
http://poj.org/problem?id=3415 Common Substrings Time Limit: 5000MS Memory Limit: 65536K Total Sub ...
- POJ 3415 后缀数组+单调栈
题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉 ...
- BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】
题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...
随机推荐
- 怎么解决/bin/sh: arm-linux-gcc: not found make
1.arm-linux-gcc 环境变量没有设,所以找不到这个编译器 在/etc/profile里添加arm-linux-gcc的存放路径 sudo -s gedit /etc/profile 编 ...
- iOS学习之事件处理
一.事件的基本概念 1.事件概述 事件是当用户手指触击屏幕及在屏幕上移动时,系统不断发送给应用程序的对象. 系统将事件按照特定的路径传递给可以对其进行处理的对象. 在iOS汇总,一个UITo ...
- TCP/IP,HTTP,Socket的区别与联系
一 忆往昔,尽是悔恨泪. 在学校的时候学过,网络七层,也知道tcp的三次握手.但因为根本没用在实际开发中,所以逐渐淡忘.现在就再次理解下三个的区别与联系. 二 正题 网络七层: ...
- 慎把“DataContext”静态化 或则单例
之前在项目里由于把DataContext静态化,最后在测试阶段发现了很多奇怪的问题,后来经过同事的指点 然后上网搜了一翻终于发现 MSDN上说: "请不要试图重用 DataContext ...
- 12.Warning (15714): Some pins have incomplete I/O assignments. Refer to the I/O Assignment Warnings report for details
解释:对于一些管脚,缺少了部分描述,需要再添加一些设置,比如current strength,slew rate等: 措施:打开pin plannel界面,在current strength和slew ...
- 最基础的Hash
type thash=^node; node=record state:longint; next:thash; end; var a,i:longint; p:thash; hash:..]of t ...
- 如何做一个脚本自动打开IE浏览器
打开记事本,输入start iexplore "http://www.baidu.com"这个是打开百度,如果只要打开IE就输入start iexplore然后另存为--保存类型改 ...
- HTML5中的Canvas精品教程
http://javascript.ruanyifeng.com/htmlapi/canvas.html
- Gentoo安装配置过程与总结
前些时间在VMware上安装了Gentoo Linux,用了当前最新版的Gentoo,安装过程记录下来了,但一直没有整理到blog上.今天重新整理一下,写出来与大家分享和备用.接触Gentoo不久,对 ...
- 【First Missing Positive】cpp
题目: Given an unsorted integer array, find the first missing positive integer. For example,Given [1,2 ...