洛谷3181 HAOI2016找相同字元 (SA+單調棧)
阿新 • • 發佈:2018-12-23
QWQ好自閉的題目!
一個題解都看不懂!!!!
貌似這種求
的程式碼實現是全網第一個?
QWQ
總之我沒有見過類似的啊。
首先這個題,我們這麼考慮。
由於是兩個串,很自然的想到把第二個串拼到第一個串的後面,然後中間新增一個非法字元。
那麼我們應該怎麼求呢?
首先想一個複雜度不是那麼優秀的做法。
我們可以直接暴力列舉任意兩個字尾,一個屬於A串,另一個屬於B串,他們的
就是會重複的子串個數。
但這個複雜度很明顯是不能接受的。我們考慮AHOI2013差異那個題的計算方法。
在本題中,我們對於每一個 通過單調棧求出來以它為最小值的最靠左的點 和最靠右的點 。然後對於一個 它的貢獻就是左邊的A串字尾數乘上右邊的B串字尾數+左邊的B串字尾數乘上右邊的A串字尾數
寫成式子就是
這裡之所以是這個式子的原因:第一要保證是一個端點屬於A串,一個屬於B串。另一個原因是因為對於一個擴充套件區間 來說,選擇字尾的右端點是在 而左端點是 ,因為字尾的選擇的左邊對於 是開區間(求兩個字尾的 ,應該是 )
總之細節很多
我果然是好菜好菜
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define int long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2e6+1e2;
struct Node{
int val,pos;
};
int wb[maxn],sa[maxn];
int tmp[maxn],rk[maxn];
char a[maxn];
int n,m;
int h[maxn],height[maxn];
int len,len1;
char s[maxn],s1[maxn];
int l[maxn],r[maxn];
Node st[maxn];
int top;
int ans;
void getsa()
{
int *x=rk,*y=tmp;
int s=128;
int p=0;
for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i;
for (int i=1;i<=s;i++) wb[i]=0;
for (int i=1;i<=n;i++) wb[x[y[i]]]++;
for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
for (int j=1;p<n;j<<=1)
{
p=0;
for (int i=n-j+1;i<=n;i++) y[++p]=i;
for (int i=1;i<=n;i++) if(sa[i]>j) y[++p]=sa[i]-j;
for (int i=1;i<=s;i++) wb[i]=0;
for (int i=1;i<=n;i++) wb[x[y[i]]]++;
for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--] = y[i];
swap(x,y);
p=1;
x[sa[1]]=1;
for (int i=2;i<=n;i++)
{
x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p;
}
s=p;
}
for (int i=1;i<=n;i++) rk[sa[i]]=i;
h[0]=0;
for (int i=1;i<=n;i++)
{
h[i]=max(h[i-1]-1,0ll);
while (i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
}
for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
int suma[maxn],sumb[maxn];
int geta(int r,int l)
{
if (!l) return suma[r];
return suma[r]-suma[l-1];
}
int getb(int r,int l)
{
if (!l) return sumb[r];
return sumb[r]-sumb[l-1];
}
signed main()
{
scanf("%s",s+1);
scanf("%s",s1+1);
len=strlen(s+1);
len1=strlen(s1+1);
for (int i=1;i<=len;i++) a[++n]=s[i];
a[++n]='*';
for (int i=1;i<=len1;i++) a[++n]=s1[i];
getsa();
for (int i=1;i<=n;i++)
{
if(sa[i]<=len) suma[i]++;
else if (sa[i]>len+1) sumb[i]++;
}
for (int i=1;i<=n;i++) suma[i]+=suma[i-1];
for (int i=1;i<=n;i++) sumb[i]+=sumb[i-1];
l[1]=1;
top=1;
st[top].val=height[1];
st[top].pos=1;
for (int i=2;i<=n;i++)
{
while (top>=1 && height[i]<=st[top].val) top--;
if (!top) l[i]=1;
else l[i] = st[top].pos+1;
st[++top].pos=i;
st[top].val=height[i];
}
r[n]=n;
top=1;
st[top].val=height[n];
st[top].pos=n;
for (int i=n-1;i>=1;i--)
{
while (top>=1 && height[i]<st[top].val) top--;
if (!top) r[i]=n;
else r[i]=st[top].pos-1;
st[++top].val=height[i];
st[top].pos=i;
}
for (int i=1;i<=n;i++)
{
ans=ans+height[i]*(geta(i-1,l[i]-1)*getb(r[i],i)+getb(i-1,l[i]-1)*geta(r[i],i));
//cout<<ans<<" "<<height[i]<<" "<<l[i]<<" "<<r[i]<<