【题目链接】

  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(后缀数组,单调栈 | 后缀自动机)的更多相关文章

  1. 【BZOJ-3238】差异 后缀数组 + 单调栈

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1561  Solved: 734[Submit][Status] ...

  2. BZOJ_3879_SvT_后缀数组+单调栈

    BZOJ_3879_SvT_后缀数组+单调栈 Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个 ...

  3. BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈

    BZOJ_3238_[Ahoi2013]差异_后缀数组+单调栈 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao ...

  4. BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)

    BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...

  5. 【BZOJ3879】SvT 后缀数组+单调栈

    [BZOJ3879]SvT Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干 ...

  6. BZOJ3238 [Ahoi2013]差异 【后缀数组 + 单调栈】

    题目链接 BZOJ3238 题解 简单题 经典后缀数组 + 单调栈套路,求所有后缀\(lcp\) #include<iostream> #include<cstdio> #in ...

  7. POJ3415 Common Substrings —— 后缀数组 + 单调栈 公共子串个数

    题目链接:https://vjudge.net/problem/POJ-3415 Common Substrings Time Limit: 5000MS   Memory Limit: 65536K ...

  8. poj 3415 Common Substrings(后缀数组+单调栈)

    http://poj.org/problem?id=3415 Common Substrings Time Limit: 5000MS   Memory Limit: 65536K Total Sub ...

  9. POJ 3415 后缀数组+单调栈

    题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉 ...

  10. BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】

    题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...

随机推荐

  1. extjs panel自动滚动

    指定两个参数 height:600, autoScroll:true, http://bbs.csdn.net/topics/280012147

  2. openSUSE install failed

    install openSUSE 13.1 with vmware play (version 6.0.0)  calling the yast module 'inst_autoinit has f ...

  3. Android编程: fragment组件、菜单和Intent组件

    学习内容:fragment组件.菜单和Intent组件 ====fragment组件====1.fragment是一种自我容纳,模块化的,嵌入在一个Activity里面的视图组件    可以在运行时动 ...

  4. [SSH服务]——一个SSH无密码登陆实验

    实验拓扑图 实验描述 机房内有两台服务器: (1)B服务器10.0.10.158,充当Web服务器,有普通用户user_00 (2)C服务器10.0.10.191,充当Mysql服务器,有普通用户us ...

  5. jQuery插件开发总结

    jQuery插件的开发包括两种: 一种是类级别的插件开发$.extend,即给jQuery添加新的全局函数,相当于给jQuery类本身添加方法,比如:$.ajax, $.getJSON等.jQuery ...

  6. MVC中使用SignalR打造酷炫实用的即时通讯功能附源码

    前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯.由于当时走的太急,忘记把代码拿出来.想想这已经是大半年前的事情了,时间过 ...

  7. C++ 排序函数 sort(),qsort()的含义与用法 ,字符串string 的逆序排序等

    上学时我们很多学了很多种排序算法,不过在c++stl中也封装了sort等函数,头文件是#include <algorithm> 函数名 功能描述 sort 对给定区间所有元素进行排序 st ...

  8. Java 7 中 NIO.2 的使用——第二节 元数据文件的属性

    如果你有很多疑问关于一个文件或目录,它是否是隐藏的,它的大小是多少,谁拥有它,你可以从元数据中得到这些信息.所谓的元数据,就是描述数据的数据. NIO.2组织了这些原数据的属性的概念,并提供了java ...

  9. python 交换变量值

    在其他语言中,交换两个变量值的时候,可以这样写: temp = a a = b b = temp 在Python中,我们可以简单的这样写: a,b=b,a 实验如下: >>> a=1 ...

  10. JS正则表达式 替换括号,尖括号等

    function toTxt(str) { var RexStr = /\<|\>|\"|\'|\&/g str = str.replace(RexStr, functi ...