POJ 3415-Common Substrings(字尾陣列+單調棧-公共子串的長度)
Common Substrings
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 10850 | Accepted: 3587 |
Description
A substring of a string T is defined as:
Given two strings A, B and one integer K, we define S, a set of triples (i, 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
題目意思:
給出兩個字串,計算它們所有的長度大於K的公共子串的個數(可以重複)。
解題思路:
只想到用字尾陣列,妥妥會TLE,看了一下大神說加單調棧優化,單調棧的思路不難,看的時候就是不明白如何用它來優化…(*゜ー゜*)
按照之前求最長公共子串長度的題目,兩串加‘$’連線後求得高度陣列,然後分B去匹配A、A去匹配B兩種情況來掃描,其實思路是一樣的,所以舉一個B去匹配A栗子來說明。
『
_( ゚Д゚)ノ首先必須要滿足的條件是①最長公共子串長度大於K;②分屬於兩個不同字串即A串和B串。
當A的字尾與B的字尾的最長公共字首長度(最長公共子串長度)大於K,且當前是A串的位置時,個數+=高度陣列lcp[i]-長度限制K+1,因為長度範圍在[K, ]均滿足題意,區間內個數是lcp[i]-K+1。
單調棧維護一個棧頂是不小於K的最小公共字首長度的高度陣列及其對應個數的序列,每次如果當前最小公共字首長度小於棧頂元素則需要調整:去掉出棧元素多加了的個數、滿足條件的A字尾串的個數加上出棧元素對應的個數。
因為現在棧頂元素變的更小了,所以更小的元素肯定是包含了之前那個比它大的元素對應的“公共子串的個數”,我們就要在調整的過程中減去這部分被重複計算的元素個數。
最後需要再次判斷相鄰字尾是否屬於B串,此時滿足分屬於兩個不同字串,即A串和B串。
』
同理對A去匹配B再次掃描,區別是先判斷屬於B串再判斷屬於A串來判定分屬於兩個不同字串,將兩次掃描的結果相加。
Note:小心地參考了若干大神的部落格以及傳遍大江南北的字尾陣列論文。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
#define MAXN 200100
int n,k,m,lens;
long long ans=0;
string s,t;
int sa[MAXN],lcp[MAXN];
int rank[MAXN*2],tmp[MAXN*2];
void construct_lcp(string s,int sa[],int lcp[])
{
int n=s.length();
for(int i=0; i<=n; ++i)
rank[sa[i]]=i;
int h=0;
lcp[0]=0;
for(int i=0; i<n; ++i)
{
int j=sa[rank[i]-1];
if(h>0) --h;
for(; j+h<n&&i+h<n; ++h)
if(s[j+h]!=s[i+h]) break;
lcp[rank[i]-1]=h;
}
}
bool compare_sa(int i,int j)//倍增法,比較rank
{
if(rank[i]!=rank[j]) return rank[i]<rank[j];
else
{
int ri=i+k<=n?rank[i+k] :-1;
int rj=j+k<=n?rank[j+k] :-1;
return ri<rj;
}
}
void construct_sa(string s,int sa[])//計算s的字尾陣列
{
for(int i=0; i<=n; ++i)//初始長度為1,rank為字元編碼
{
sa[i]=i;
rank[i]=i<n?s[i] :-1;
}
for(k=1; k<=n; k*=2)//倍增法求字尾陣列
{
sort(sa,sa+n+1,compare_sa);
tmp[sa[0]]=0;
for(int i=1; i<=n; ++i)
tmp[sa[i]]=tmp[sa[i-1]]+(compare_sa(sa[i-1],sa[i])?1:0);
for(int i=0; i<=n; ++i)
rank[i]=tmp[i];
}
}
bool contain(string s,int sa[],string t)
{
int a=0,b=s.length();
while(b-a>1)
{
int c=(a+b)/2;
if(s.compare(sa[c],t.length(),t)<0) a=c;
else b=c;
}
return s.compare(sa[b],t.length(),t)==0;
}
void solve()
{
int dull[MAXN][2];//維護字尾的單調遞減棧,棧頂最小,dull[i][0]是lcp[i],dull[i][1]是個數
long long temp,top;//temp記錄當前棧中所有項和一個剛進入的子串匹配所能得到的總的子串的數目
ans=0;
//第一次掃描,B串中的子串匹配rank比其高的A子串
for(int i=0; i<n; i++) //每遇到一個B的字尾就統計與前面的A的字尾能產生多少個長度不小於k的公共子串
{
if (lcp[i]<m) top=temp=0;
else//A的字尾與B的字尾的最長公共字首長度滿足限制條件
{
int res=0;//滿足條件的A字尾串的個數
if(sa[i]<lens)//在第一個串中
{
++res;
temp+=lcp[i]-m+1;//更新個數,長度範圍在[m,最長公共字首長度]均滿足題意
}
while(top>0&&lcp[i]<=dull[top-1][0])//調整單調棧,當前最長公共字首長度比棧頂元素還小
{
--top;
temp-=dull[top][1]*(dull[top][0]-lcp[i]);//去掉出棧元素多加了的個數
res+=dull[top][1];
}
dull[top][0]=lcp[i];
dull[top++][1]=res;
if(sa[i+1]>lens)//在第二個串中,即分屬於兩個不同字串
ans+=temp;
}
}
//第二次掃描,A串中的子串匹配rank比其高的B子串
for(int i=0; i<n; i++)
{
if (lcp[i]<m) top=temp=0;
else
{
int res=0;//滿足條件的B字尾串的個數
if(sa[i]>lens)//在第二個串中
{
++res;
temp+=lcp[i]-m+1;
}
while (top>0&&lcp[i]<=dull[top-1][0])
{
--top;
temp-=dull[top][1]*(dull[top][0]-lcp[i]);
res+=dull[top][1];
}
dull[top][0]=lcp[i];
dull[top++][1]=res;
if(sa[i+1]<lens)//在第一個串中,即分屬於兩個不同字串
ans+=temp;
}
}
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("G:/cbx/read.txt","r",stdin);
//freopen("G:/cbx/out.txt","w",stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>m)
{
if(m==0) break;
cin>>s;
cin>>t;
lens=s.length();
s+='$'+t;//連線串
n=s.length();
construct_sa(s,sa);
construct_lcp(s,sa,lcp);
solve();
cout<<ans<<endl;
}
return 0;
}
相關文章
- POJ 2217-Secretary(字尾陣列+高度陣列-最大公共子串長度)陣列
- [POJ 3415] Common Substrings (字尾陣列+單調棧優化)陣列優化
- poj 2774 求兩字串的最長公共子串 字尾陣列字串陣列
- POJ 2774-Long Long Message(字尾陣列+高度陣列-最大公共子串長度)陣列
- poj 3415 Common Substrings(長度大於k的相同子串對數xian 字尾陣列+單調桟統計)陣列
- POJ 3294 Life Forms(字尾陣列求k個串的最長子串)ORM陣列
- POJ 3693 Maximum repetition substring(字尾陣列求最長重複子串)陣列
- POJ 1743 Musical Theme (字尾陣列,求最長不重疊重複子串)陣列
- HDU 4622 Reincarnation( 任意區間子串的長度, 字尾陣列+RMQ)陣列MQ
- poj3080-kmp+列舉子串 求最長公共子串KMP
- POJ 3415 Common Substrings(字尾陣列求重複字串)陣列字串
- ural 1297 最長迴文子串 字尾陣列陣列
- SPOJ 687. Repeats(字尾陣列求最長重複子串)陣列
- Codeforces #123D: 字尾陣列+單調棧3D陣列
- 最長公共子串 二維陣列 Go實現陣列Go
- SPOJ 694. Distinct Substrings,705. New Distinct Substrings(字尾陣列)陣列
- POJ 3581-Sequence(字尾陣列)陣列
- POJ 1743 Musical Theme(字尾陣列)陣列
- lCS(最長公共子串)
- HDU 5769-Substring(字尾陣列-不相同的子串的個數)陣列
- 【Kmp求既是字首又是字尾的子串】POJ - 2752 Seek the Name, Seek the FameKMP
- SPOJ 220. Relevant Phrases of Annihilation(字尾陣列多次不重疊子串)陣列
- 兩個字串的最長公共子串字串
- 線性dp:最長公共子串
- POJ1743 Musical Theme(字尾陣列 二分)陣列
- POJ2774Long Long Message(字尾陣列模板)陣列
- 字尾陣列模板陣列
- 字尾陣列 SA陣列
- 字尾陣列,SA陣列
- SPOJ 694 求一個字串有多少子串 字尾陣列字串陣列
- 203. 長度最小的子陣列陣列
- 字尾陣列複習陣列
- 字尾陣列(後續)陣列
- 字尾陣列詳解陣列
- 【筆記】字尾陣列筆記陣列
- URAL 1297. Palindrome(字尾陣列求最大回文串)陣列
- DreamJudge-1294-字尾子串排序排序
- 每日一練(45):長度最小的子陣列陣列