poj 3415 Common Substrings (后缀自动机+parent树上的lazy标记)

http://poj.org/problem?id=3415

题意:

给出两个字符串,问公共长度大于等于k的子串个数(只要两个串的位置不同就认为是不同)

思路:

考虑SAM的性质。

SAM上的一个节点所能接受的本质不同的子串个数是**st[v].len - st[st[v].link].len**

而这些子串,都出现了right[v]次,因为不同子串的个数就是**(st[v].len-st[st[v].link].len)right[v]*

现在有了限制条件,要求长度大于等于k.

没有限制的话,SAM上的一个节点所能接受的字符串的长度范围是在[st[st[v].link].len+1,st[v].len]

那么现在范围其实就变成了**[MX,st[v].len],其中MX = max{st[st[v].link].len+1,k}**

对于A串构建SAM,然后B串在SAM上运行

考虑对于SAM的某个状态,B串此时的最大匹配长度为len,那么len>=MX时,满足条件的字符串的范围就变成了**[MX,len] **

len<MX时无贡献。

所以该状态(v)对答案的就是  (len-MX+1)*right[v]

然而这还不算完,和之前的LCS2一样,如果SAM上的一个节点能匹配字符串B的长度大于等于k,那么该节点的祖先节点(父亲节点,父亲的父亲的节点…)

能匹配的字符串B的长度也都大于等于k…

如果我们一边匹配一边沿着parent树自底向上传递的话…复杂度n^2,一首凉凉送给自己

但是正常人会这样?.jpg

我们先打个标记,最后沿parent树自底向上把标记传递上去就行了。。

需要注意的是,此题的字符集是大小写字母都会包含…RE了2发orz

  1/* ***********************************************
  2Author :111qqz
  3Created Time :2017年11月08日 星期三 18时50分18秒
  4File Name :3518.cpp
  5************************************************ */
  6
  7//#include <bits/stdc++.h>
  8#include <cstring>
  9#include <string>
 10#include <algorithm>
 11#include <iostream>
 12#include <cstdio>
 13#include <cmath>
 14#define PB push_back
 15#define fst first
 16#define sec second
 17#define lson l,m,rt<<1
 18#define rson m+1,r,rt<<1|1
 19#define ms(a,x) memset(a,x,sizeof(a))
 20typedef long long LL;
 21#define pi pair < int ,int >
 22#define MP make_pair
 23
 24using namespace std;
 25const double eps = 1E-8;
 26const int dx4[4]={1,0,0,-1};
 27const int dy4[4]={0,-1,1,0};
 28const int inf = 0x3f3f3f3f;
 29#define MAXALP 55 //还有大写字母orz
 30int k;
 31struct state
 32{
 33    int len, link, nxt[MAXALP];
 34};
 35const int N =1E5+7;
 36state st[N*2];
 37int sz, last,rt;
 38char s[N];
 39int Right[2*N];
 40int cnt[2*N],rk[2*N];//for radix sort
 41int dp[2*N],lazy[2*N];
 42void sa_init()
 43{
 44    sz = 0;
 45    last = rt = ++sz;
 46    st[1].len = 0;
 47    st[1].link=-1;
 48    ms(st[1].nxt,-1);
 49}
 50void sa_extend(int c)
 51{
 52    int cur = ++sz;
 53    st[cur].len = st[last].len + 1;
 54    memset(st[cur].nxt, -1, sizeof(st[cur].nxt));
 55    int p;
 56    for (p = last; p != -1 && st[p].nxt[c] == -1; p = st[p].link)
 57        st[p].nxt[c] = cur;
 58    if (p == -1) {
 59        st[cur].link = rt;
 60    } else {
 61        int q = st[p].nxt[c];
 62        if (st[p].len + 1 == st[q].len) {
 63            st[cur].link = q;
 64        } else {
 65            int clone = ++sz ;
 66            st[clone].len = st[p].len + 1;
 67            st[clone].link = st[q].link;
 68            memcpy(st[clone].nxt, st[q].nxt, sizeof(st[q].nxt));
 69            for (; p != -1 && st[p].nxt[c] == q; p = st[p].link)
 70                st[p].nxt[c] = clone;
 71            st[q].link = st[cur].link = clone;
 72        }
 73    }
 74    last = cur;
 75}
 76void topo()
 77{
 78    ms(cnt,0); 
 79    for (int i = 1 ; i <= sz ; i++) cnt[st[i].len]++;
 80    for ( int i = 1 ; i <= sz ; i++) cnt[i]+=cnt[i-1];
 81    for (int i = 1 ; i <= sz  ;i++) rk[cnt[st[i].len]--] = i;
 82}
 83char ST[N];
 84int idx( char c)
 85{
 86    if (c>='a'&&c<='z') return c-'a';
 87    return c-'A'+26;
 88}
 89int main()
 90{
 91#ifndef  ONLINE_JUDGE 
 92        freopen("./in.txt","r",stdin);
 93#endif
 94    while (~scanf("%d",&k)!=EOF)
 95    {
 96    if (k==0) break;
 97    ms(Right,0);
 98    ms(lazy,0); //parent树上的lazy标记,最后子底向上更新
 99    scanf("%s",ST);
100//  cout<<"ST:"<<ST<<endl;
101    sa_init();
102    for (int i = 0,len = strlen(ST);  i < len ; i++)
103    {
104        Right[sz+1] = 1;
105        sa_extend(idx(ST[i]));
106    }
107    topo();
108    for ( int i = sz ; i >=1 ; i--) if (st[rk[i]].link!=-1)Right[st[rk[i]].link]+=Right[rk[i]];
109    //check right
110//  for ( int i = sz ; i >=1 ; i--) printf("right:%d\n",Right[i]);
111    scanf("%s",ST);
112    int now = rt,len = 0;
113    LL ans = 0 ;
114    for ( int i = 0,_len = strlen(ST) ; i < _len ; i++)
115    {
116        int id = idx(ST[i]);
117        if (st[now].nxt[id]!=-1)
118        {
119        now = st[now].nxt[id];
120        len++;
121        //  printf("now:%d dp[now]:%d len: %d\n",now,dp[now],len);
122        }
123        else 
124        {
125        while (now!=-1&&st[now].nxt[id]==-1) now = st[now].link;
126        if (now==-1) now=rt,len=0;else len = st[now].len + 1,now=st[now].nxt[id];
127        }
128//      printf("len:%d\n",len);
129        if (len>=k)
130        {
131        if (st[now].link==-1) continue;
132        ans += 1LL*(len-max(k,st[st[now].link].len+1)+1)*Right[now];
133        if (st[st[now].link].len>=k) lazy[st[now].link]++;
134        }
135    }
136
137    for ( int i = sz ; i >= 1 ; i-- )
138    {
139        int v = rk[i];
140        if (st[v].link==-1) continue;
141        ans += 1LL*lazy[v]*(st[v].len-max(k,st[st[v].link].len+1)+1)*Right[v];
142        if (st[st[v].link].len>=k) lazy[st[v].link] += lazy[v]; //lazy标记沿着parent树向上传递
143    }
144    printf("%lld\n",ans);
145    }
146
147
148
149
150
151#ifndef ONLINE_JUDGE  
152    fclose(stdin);
153#endif
154    return 0;
155}