[洛谷P3181] [HAOI2016]找相同字符
洛谷題目鏈接:[HAOI2016]找相同字符
題目描述
給定兩個字符串,求出在兩個字符串中各取出一個子串使得這兩個子串相同的方案數。兩個方案不同當且僅當這兩個子串中有一個位置不同。
輸入輸出格式
輸入格式:
兩行,兩個字符串s1,s2,長度分別為n1,n2。1 <=n1, n2<= 200000,字符串中只有小寫字母
輸出格式:
輸出一個整數表示答案
輸入輸出樣例
輸入樣例#1:
aabb
bbaa
輸出樣例#1:
10
題意: 將\(s2\)接在\(s1\)的後面,問\[\sum _{i\in[1,n1],j\in[n1+1,n1+n2]}lcp(suffix(i),suffix(j))\]
題解: 首先考慮如何求出任意兩後綴的\(LCP\)(例題[AHOI2013]差異),首先我們知道\(LCP(i, j) = min\{lcp(k-1, k)\},k\in [i+1, j]\).也就是我們可以通過ST表預處理\(height\)數組的最小值,然後對於兩個後綴\(O(1)\)查詢.然而這裏枚舉兩個後綴的時間復雜度是\(O(n^2)\)的,也就是說這樣求是不行的.
我們再觀察一下這個式子:\(LCP(i, j) = min\{lcp(k-1, k)\},k\in [i+1, j]\),可以發現,如果我們求出\(i\)左邊第一個小於\(height[i]\)的位置\(L[i]\),右邊第一個小於等於\(height[i]\)
那麽如果將\(s2\)接在\(s1\)的後面,求一下任意兩後綴的\(LCP\)之和,那麽總答案就被統計出來了.
然而這樣統計的話會有個問題:\(s1\)內的後綴互相匹配,\(s2\)內的後綴也互相匹配,導致答案增加,所以我們要減掉這一部分的答案.
然而這樣統計依然有問題.我們考慮這樣兩個串:\(s1="aaaa",s2="aaaa\)
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+5;
typedef int _int;
#define int long long
int n, m, l1, l2, L[N], R[N], stk[N], top = 0, ans = 0;
int sa[N], rk[N], sec[N], buk[N], height[N];
char s1[N], s2[N], s[N];
void rsort(){
for(int i = 0; i <= m; i++) buk[i] = 0;
for(int i = 1; i <= n; i++) buk[rk[i]]++;
for(int i = 1; i <= m; i++) buk[i] += buk[i-1];
for(int i = n; i >= 1; i--) sa[buk[rk[sec[i]]]--] = sec[i];
}
void SuffixArray(){
for(int i = 1; i <= n; i++) rk[i] = s[i], sec[i] = i;
int cnt = 0; m = 300, rsort();
for(int l = 1; l <= n && cnt < n; l <<= 1){
cnt = 0;
for(int i = 1; i <= l; i++) sec[++cnt] = n-l+i;
for(int i = 1; i <= n; i++) if(sa[i] > l) sec[++cnt] = sa[i]-l;
rsort(); swap(sec, rk), rk[sa[1]] = cnt = 1;
for(int i = 2; i <= n; i++)
rk[sa[i]] = (sec[sa[i]] == sec[sa[i-1]] && sec[sa[i]+l] == sec[sa[i-1]+l]) ? cnt : ++cnt;
m = cnt;
}
}
void get_height(){
int j, k = 0;
for(int i = 1; i <= n; i++){
if(k) k--;
j = sa[rk[i]-1];
while(s[i+k] == s[j+k]) k++;
height[rk[i]] = k;
}
}
int solve(){
memset(L, 0, sizeof(L)), memset(R, 0, sizeof(R));
memset(sa, 0, sizeof(sa)), memset(rk, 0, sizeof(rk));
memset(height, 0, sizeof(height));
memset(sec, 0, sizeof(sec));
SuffixArray(), get_height();
int res = 0; stk[top = 0] = 1;
for(int i = 2; i <= n; i++){
while(top && height[stk[top]] >= height[i]) top--;
L[i] = stk[top], stk[++top] = i;
}
stk[top = 0] = n+1;
for(int i = n; i >= 2; i--){
while(top && height[stk[top]] > height[i]) top--;
R[i] = stk[top], stk[++top] = i;
}
for(int i = 2; i <= n; i++) res += height[i]*(i-L[i])*(R[i]-i);
cerr << "res=" << res << endl;
return res;
}
_int main(){
scanf("%s%s", s1+1, s2+1), l1 = strlen(s1+1), l2 = strlen(s2+1);
for(int i = 1; i <= l2; i++) s[i] = s2[i];
n = l2; ans -= solve();
for(int i = 1; i <= l1; i++) s[i] = s1[i];
n = l1; ans -= solve();
for(int i = 1; i <= l2; i++) s[i+l1+1] = s2[i];
s[l1+1] = '#', n = l1+l2+1; ans += solve();
cout << ans << endl;
return 0;
}
[洛谷P3181] [HAOI2016]找相同字符