1. 程式人生 > >洛谷3181 HAOI2016找相同字元 (SA+單調棧)

洛谷3181 HAOI2016找相同字元 (SA+單調棧)

題目連結

QWQ好自閉的題目!
一個題解都看不懂!!!!
貌似這種求 a n s ans 的程式碼實現是全網第一個?

QWQ
總之我沒有見過類似的啊。

首先這個題,我們這麼考慮。

由於是兩個串,很自然的想到把第二個串拼到第一個串的後面,然後中間新增一個非法字元。

那麼我們應該怎麼求呢?

首先想一個複雜度不是那麼優秀的做法。
我們可以直接暴力列舉任意兩個字尾,一個屬於A串,另一個屬於B串,他們的 l c p lcp 就是會重複的子串個數。

但這個複雜度很明顯是不能接受的。我們考慮AHOI2013差異那個題的計算方法。

在本題中,我們對於每一個 h

e i g h t height 通過單調棧求出來以它為最小值的最靠左的點 l [
i ] l[i]
和最靠右的點 r [ i ] r[i] 。然後對於一個 h e i g h t height 它的貢獻就是左邊的A串字尾數乘上右邊的B串字尾數+左邊的B串字尾數乘上右邊的A串字尾數

寫成式子就是
h e i g h t [ i ] ( g e t a ( i 1 , l [ i ] 1 ) g e t b ( r [ i ] , i ) + g e t b ( i 1 , l [ i ] 1 ) g e t a ( r [ i ] , i ) ) height[i]*(geta(i-1,l[i]-1)*getb(r[i],i)+getb(i-1,l[i]-1)*geta(r[i],i))

這裡之所以是這個式子的原因:第一要保證是一個端點屬於A串,一個屬於B串。另一個原因是因為對於一個擴充套件區間 [ l , p o s , r ] [l,pos,r] 來說,選擇字尾的右端點是在 [ p o s , r ] [pos,r] 而左端點是 [ l 1 , p o s 1 ] [l-1,pos-1] ,因為字尾的選擇的左邊對於 h e i g h t height 是開區間(求兩個字尾的 l c p lcp ,應該是 m i n ( h e i g h t [ r k [ i ] + 1 ] , h e i g h t [ r k [ i ] + 2 ] . . . . . h e i g h t [ r k [ j ] ] ) min(height[rk[i]+1],height[rk[i]+2].....height[rk[j]])

總之細節很多

我果然是好菜好菜

#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]<<