Common Substrings
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 9781 Accepted: 3222
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
鸣谢:http://blog.csdn.net/acm_cxlove/article/details/7946967
因为这篇博客的思路我才能有所了解。
题意大概是:
求出两个字符串中不少于k个公共子串的个数
单调队列就是指队列中保证队列里的元素是单调的。
思路就是: 先将两个字符串连接成一个字符串,并在中间用一个未出现的字符将其隔开,并在末尾用一个字符 ‘\0’ 将其结尾(这是论文中说的以0位结尾)
在求出后缀数组之后,从头到尾扫描一遍,top表示为栈顶下标。
用tot 记录当前height所能为答案贡献的长度。
如 height[3]=4,k=2,那么height[3]所能贡献的长度为 4-2+1=3
但是如果过程中出现了小于k的情况那么将tot,top归零。
如果出现了当前的height{i]与栈中的值小的话,难么就减去那个差值。
s[top][0]表示上一个height[]元素的值,s[top][1]表示上次的cnt的值
大家可以手写一遍check()函数的过程就大概理解了。(不要问我是怎么知道的……..我就是这么干的。。。。。。。。。)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <fstream>
#include<cmath>
#define N 100005
#define LL long long
#define maxn 200005
#define MME(i,j) memset(i,j,sizeof(i))
using namespace std;
//以下为倍增算法求后缀数组
int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(const char *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0; i<m; i++) Ws[i]=0;
for(i=0; i<n; i++) Ws[x[i]=r[i]]++;
for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
for(i=n-1; i>=0; i--) sa[--Ws[x[i]]]=i;
for(j=1,p=1; p<n; j*=2,m=p)
{
for(p=0,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<n; i++) wv[i]=x[y[i]];
for(i=0; i<m; i++) Ws[i]=0;
for(i=0; i<n; i++) Ws[wv[i]]++;
for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
/* ofstream file("SA.txt");
for(int i=0; i<n; i++)
file<<"SA["<<i<<"] is "<<sa[i]<<"\n";
file.close();
for(int i=0; i<=n; i++)
printf("SA[%d] is %d\n",i,sa[i]);*/
return;
}
int sa[maxn],Rank[maxn],height[maxn];
//求height数组
void calheight(const char *r,int *sa,int n)
{
int i,j,k=0;
for(i=1; i<=n; i++) Rank[sa[i]]=i;
for(i=0; i<n; height[Rank[i++]]=k)
for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);
/* for(int i=0; i<=n; i++)
printf("h[%d] is %d\n",i,height[i]);*/
return;
}
char str[maxn],ch[maxn];
int k;
int s[maxn][2];
LL tot,top;
LL check(int len,int lenA)
{
tot=top=0;
MME(s,0);
LL sum=0;
for(int i=1; i<=len; i++)
{
if(height[i]<k) tot=top=0;
else
{
int cnt=0;
if(sa[i-1]<lenA) cnt++, tot+=height[i]-k+1;
while(top>0&&height[i]<=s[top-1][0])
{
top--;
tot-= s[top][1]*(s[top][0]-height[i]);
cnt+= s[top][1];
}
s[top][0]=height[i],s[top++][1]=cnt;
if(sa[i]>lenA)
sum+=tot;
}
}
tot=top=0;
for(int i=1; i<=len; i++)
{
if(height[i]<k) tot=top=0;
else
{
int cnt=0;
if(sa[i-1]>lenA) cnt++,tot+=height[i]-k+1;
while(top>0&&height[i]<=s[top-1][0])
{
top--;
tot-= s[top][1]*(s[top][0]-height[i]);
cnt+= s[top][1];
}
s[top][0]=height[i],s[top++][1]=cnt;
if(sa[i]<lenA)
sum+=tot;
}
}
return sum;
}
int main()
{
while(scanf("%d",&k)!=EOF&&k)
{
int l1,l2;
scanf("%s%s",str,ch);
l1=strlen(str);
l2=strlen(ch);
str[l1]='@';
for(int i=l1+1; i<=l1+l2; i++)
str[i]=ch[i-l1-1];
int n=l1+l2+1;
str[n]='\0';
da(str,sa,n+1,130);
calheight(str,sa,n);
LL sum;
sum=check(n,l1);
printf("%I64d\n",sum);
}
return 0;
}