【题目链接】

  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. ssh公钥免密码登录

    1,生成公钥 ssh-keygen -t rsa 2,上传至服务器 将个人电脑的公钥添加到服务器 cat id_rsa.pub >> ~/.ssh/authorized_keys 3,本地 ...

  2. 你必须懂的 T4 模板:深入浅出

    示例代码:示例代码__你必须懂的T4模板:浅入深出.rar (一)什么是T4模板? T4,即4个T开头的英文字母组合:Text Template Transformation Toolkit. T4文 ...

  3. JavaScript AJAX stream 流式显示

      当使用AJAX进行信息交互的时候,如果服务器返回的信息比较大,那么相对于传送完成之后的统一显示,流式显示就比较友好了. 流式实现 原理就是设置定时器,定时的查看AJAX对象的状态并更新内容,如果传 ...

  4. 浅谈dynamic的简单使用用法

    今天看了博客园里面的dynamic用法,我犹豫从来没接触过,今天恶补了一下,把我对dynamic的认识分享了出来,大家一起学习. Visual C# 2010 引入了一个新类型 dynamic. 该类 ...

  5. 41.使用Chipscope时如何防止reg_wire型信号被优化掉

    随着FPGA设计复杂程度越来越高,芯片内部逻辑分析功能显得越来越重要.硬件层次上的逻辑分析仪价格十分昂贵,而且操作比较复杂.目前,FPGA芯片的两大供应商都为自己的FPGA芯片提供了软件层面上的逻辑分 ...

  6. 查看BADI有哪些实现

    TCODE:SE18

  7. js 完成对图片的等比例缩放的方法

    /* 重新按比例设置 页面上对应图片的长和高, */ function resetImgSize(id,imgWidth,imgHeight,posWidth,posHeight) { var wid ...

  8. LeetCode Shell Problems

    195. Tenth Line -- 第十行 How would you print just the 10th line of a file? Solution: awk 'NR==10' file ...

  9. 解决jquery-easyui1.3.3 combobox 多选模式不兼容IE8问题

    扩展Array的原型对象,加入indexOf方法 if(!Array.prototype.indexOf){    Array.prototype.indexOf = function(target) ...

  10. python-操作mssql数据库

    准备工作: cmd 命令行下安装pymssql: pip install pymssql 查询的数据库如下: 代码如下: #coding=utf-8 import pymssql class MSSQ ...