Description
A substring of a string T is defined as:
T
(
i
,
k
)
=
T
i
T
i
+
1
.
.
.
T
i
+
k
−
1
,
1
≤
i
≤
i
+
k
−
1
≤
∣
T
∣
.
T( i, k)= T_iT_{i +1}... T_{i+k -1}, 1≤ i≤ i+k-1≤| T|.
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
Source
POJ Monthly–2007.10.06, wintokk
sol:
题目是让我们求A的后缀和B的后缀的所有lcp中lcp>=K的lcp-k的和。
用后缀数组来做
考虑维护一个并查集,并起来的点是一系列相邻的点,并查集中的元素是这个集合中最靠左的点,最左边点的hei,集合中A的后缀的数量numa,B的后缀的数量numb,将hei从大到小来处理,对每次处理的hei,将这个hei指向的两个相邻的点对应的并查集计算贡献,并且合并。
因为hei从大到小考虑,所以一个并查集中的hei一定是大于当前枚举的hei的,当前的hei可以控制左右两个集合的hei。
类似分治的计算贡献
但本质上是在后缀树上做dp
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=210000;
int T,n,m;
int sa[N],rk[N],w[N],x[N],hei[N];
char s[N];
int sr[N];
inline void build_sa()
{
memset(x,0,sizeof(x));
memset(w,0,sizeof(w));
memset(sa,0,sizeof(sa));
memset(rk,0,sizeof(rk));
memset(hei,0,sizeof(hei));
int m=4200;
for(int i=1;i<=n;++i) ++w[x[i]=sr[i]];
for(int i=1;i<=m;++i) w[i]+=w[i-1];
for(int i=n;i>=1;--i) sa[w[x[i]]--]=i;
for(int j=1;j<=n;j<<=1)
{
int cnt=0;
for(int i=n-j+1;i<=n;++i) rk[++cnt]=i;
for(int i=1;i<=n;++i)
if(sa[i]-j>0) rk[++cnt]=sa[i]-j;
for(int i=1;i<=m;++i) w[i]=0;
for(int i=1;i<=n;++i) ++w[x[i]];
for(int i=1;i<=m;++i) w[i]+=w[i-1];
for(int i=n;i>=1;--i) sa[w[x[rk[i]]]--]=rk[i];
m=0;
for(int i=1;i<=n;++i)
{
int u=sa[i],v=sa[i-1];
if(x[u]!=x[v]||x[u+j]!=x[v+j]) ++m;
rk[u]=m;
}
if(m==n) break;
for(int i=1;i<=n;++i) x[i]=rk[i];
}
int j=0;
for(int i=1;i<=n;++i)
{
j=max(j-1,0);
int u=sa[rk[i]-1];
while(sr[u+j]==sr[i+j]) ++j;
hei[rk[i]]=j;
}
}
int belong[N];
struct cc
{
int hei,pos;
cc(){}
cc(int x,int y){hei=x;pos=y;}
friend bool operator <(const cc &a,const cc &b)
{
return a.hei<b.hei;
}
};
priority_queue<cc> q;
int fa[N];
int numa[N],numb[N];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void unit(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)
{
fa[fy]=fx;
numa[fx]+=numa[fy];
numb[fx]+=numb[fy];
}
}
inline void solve()
{
n=0;
int cnt=200;
memset(numa,0,sizeof(numa));
memset(numb,0,sizeof(numb));
memset(belong,0,sizeof(belong));
for(int i=1;i<=2;++i)
{
cin.getline(s+1,N,'\n');
int t=strlen(s+1);
for(int j=1;j<=t;++j)
{
sr[++n]=s[j];
belong[n]=i;
}
sr[++n]=++cnt;
belong[n]=i;
}
build_sa();
for(int i=1;i<=n;++i)
{
fa[i]=i;
if(sr[sa[i]]>=200) continue;
numa[i]=belong[sa[i]]==1;
numb[i]=belong[sa[i]]==2;
}
while(q.size()) q.pop();
for(int i=1;i<=n;++i)
q.push((cc){hei[i],i});
ll ans=0;
while(q.size()>1)
{
cc a,b;
a=q.top();
q.pop();
int x=find(a.pos);
if(hei[x]<m) break;
if(x==1) continue;
int y=find(x-1);
ans+=(ll)numa[y]*numb[x]*(hei[x]-m+1);
ans+=(ll)numb[y]*numa[x]*(hei[x]-m+1);
unit(y,x);
}
cout<<ans<<endl;
}
int main()
{
while(1)
{
cin.getline(s+1,300,'\n');
n=strlen(s+1);
m=0;
for(int i=1;i<=n;++i) m=m*10+s[i]-'0';
if(!m) break;
solve();
}
}