Luogu P3181 [HAOI2016]找相同字符

题意:两个字符串取出相同子串的方案数,方案不同当且仅当这两个子串中有一个位置不同。

\(SA\) 后缀的前缀是子串,即求 \(\sum_{i<j} {\rm lcp}(suff_i,suff_j)\),这个可以求出 \(ht[]\) 单调栈预处理。我们先把两个串拼起来算答案,然后再减去两个串分别计算的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#define R register int
#define ll long long
using namespace std;
namespace Luitaryi {
const int N=400010;
int n,n1,n2; ll ans;
int sa[N],rk[N],ht[N],c[N],x[N],y[N],l[N],r[N],stk[N],top;
char s1[N>>1],s2[N>>1],s[N];
inline void get_sa(char* s) {
  memset(c,0,sizeof c);
  memset(y,0,sizeof y);
  memset(x,0,sizeof x);
  R m=128;
  for(R i=1;i<=n;++i) ++c[x[i]=s[i]];
  for(R i=2;i<=m;++i) c[i]+=c[i-1];
  for(R i=n;i;--i) sa[c[x[i]]--]=i;
  for(R t=1,top=0;top<n;m=top,t<<=1) {
    top=0;
    for(R i=n-t+1;i<=n;++i) y[++top]=i;
    for(R i=1;i<=n;++i) if(sa[i]>t) y[++top]=sa[i]-t;
    memset(c,0,(m+1)<<2);
    for(R i=1;i<=n;++i) ++c[x[i]];
    for(R i=2;i<=m;++i) c[i]+=c[i-1];
    for(R i=n;i;--i) sa[c[x[y[i]]]--]=y[i];
    swap(x,y),x[sa[1]]=top=1;
    for(R i=2;i<=n;++i) 
      x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+t]==y[sa[i-1]+t])?top:++top;
  } for(R i=1;i<=n;++i) rk[sa[i]]=i;
}
inline void get_ht(char* s) {
  R k=0;
  for(R i=1,j;i<=n;++i) {
    if(k) --k; j=sa[rk[i]-1];
    while(s[j+k]==s[i+k]) ++k;
    ht[rk[i]]=k;
  }
}
inline ll solve() {
  register ll ret=0;
  stk[top=1]=1;
  for(R i=2;i<=n;++i) {
    while(top&&ht[stk[top]]>ht[i]) r[stk[top--]]=i;
    l[i]=stk[top]; stk[++top]=i;
  } while(top) r[stk[top--]]=n+1; 
  for(R i=1;i<=n;++i) 
    ret+=1ll*(r[i]-i)*(i-l[i])*ht[i];
  return ret;
}
inline void main() {
  scanf("%s",s1+1),n1=strlen(s1+1);
  scanf("%s",s2+1),n2=strlen(s2+1);
  memcpy(s+1,s1+1,n1),s[n1+1]='#';
  memcpy(s+n1+2,s2+1,n2);
  n=n1+n2+1,get_sa(s),get_ht(s),ans+=solve(); 
  n=n1,get_sa(s1),get_ht(s1),ans-=solve(); 
  n=n2,get_sa(s2),get_ht(s2),ans-=solve(); 
  printf("%lld\n",ans);
}
} signed main() {Luitaryi::main(); return 0;}

\(SAM\) 每个点的贡献是 \((l-{\rm minlen}(now)+1)\times |{\rm endpos}(now)|+\sum_{p\in ancestor(now)} ({\rm maxlen}(p)-{\rm minlen}(p)+1) \times |{\rm endpos}(p)|,l为到now这个点匹配的长度\)


2020.01.09

posted @ 2020-01-10 17:37  LuitaryiJack  阅读(106)  评论(0编辑  收藏  举报