hdu 4416 Good Article Good sentence (后缀自动机)

Posted by 111qqz on Saturday, November 11, 2017

TOC

http://acm.hdu.edu.cn/showproblem.php?pid=4416

题意:

给出一个字符串A和n个字符串B,问A的子串中,不在任何一个B中出现的本质不同的子串有多少。

思路:

还是根据len搞事情

我们知道,如果不加任何条件,SAM中一个节点所表示的本质不同的子串数量是st[i].len - st[st[i].link].len

现在加了限制条件。

那么该状态中,有一些长度的字符串就会不满足条件。

我们考虑对母串A构建SAM

那么只需要维护B的所有串,对于某个状态能匹配的最大长度,设为maxlen,那么 长度为[maxlen+1,st[i].len]的字符串可以贡献答案。

如果maxlen为0,则该状态所表示的所有本质不同的子串都可以贡献答案。

维护最大匹配长度 可以参考   spoj-lcs2 解题报告 

有所不同的是不需要在每一个B中都匹配,只需要至少一个匹配就行了,因此是取所有串的最大匹配长度即可。

/* ***********************************************
Author :111qqz
Created Time :2017年11月08日 星期三 18时50分18秒
File Name :4416.cpp
************************************************ */

#include <bits/stdc++.h>
#define PB push_back
#define fst first
#define sec second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ms(a,x) memset(a,x,sizeof(a))
typedef long long LL;
#define pi pair < int ,int >
#define MP make_pair

using namespace std;
const double eps = 1E-8;
const int dx4[4]={1,0,0,-1};
const int dy4[4]={0,-1,1,0};
const int inf = 0x3f3f3f3f;
#define MAXALP 30
const int mod = 2012;
struct state
{
    int len, link, nxt[MAXALP];
};
const int N =1E5+7;
state st[N*2];
int sz, last,rt;
char s[N];
int cnt[2*N],rk[2*N];//for radix sort
int dp[2*N];
void sa_init()
{
    sz = 0;
    last = rt = ++sz;
    st[1].len = 0;
    st[1].link=-1;
    ms(st[1].nxt,-1);
}
void sa_extend(int c)
{
    int cur = ++sz;
    st[cur].len = st[last].len + 1;
    memset(st[cur].nxt, -1, sizeof(st[cur].nxt));
    int p;
    for (p = last; p != -1 && st[p].nxt[c] == -1; p = st[p].link)
        st[p].nxt[c] = cur;
    if (p == -1) {
        st[cur].link = rt;
    } else {
        int q = st[p].nxt[c];
        if (st[p].len + 1 == st[q].len) {
            st[cur].link = q;
        } else {
            int clone = ++sz ;
            st[clone].len = st[p].len + 1;
            st[clone].link = st[q].link;
            memcpy(st[clone].nxt, st[q].nxt, sizeof(st[q].nxt));
            for (; p != -1 && st[p].nxt[c] == q; p = st[p].link)
                st[p].nxt[c] = clone;
            st[q].link = st[cur].link = clone;
        }
    }
    last = cur;
}
void topo()
{
    ms(cnt,0); 
    for (int i = 1 ; i <= sz ; i++) cnt[st[i].len]++;
    for ( int i = 1 ; i <= sz ; i++) cnt[i]+=cnt[i-1];
    for (int i = 1 ; i <= sz  ;i++) rk[cnt[st[i].len]--] = i;
}
char ST[N];
int n;
int main()
{
#ifndef  ONLINE_JUDGE 
        freopen("./in.txt","r",stdin);
#endif

    int T;
    int cas = 0 ;
    cin>>T;
    while (T--)
    {
    scanf("%d",&n);
    scanf("%s",ST);
    sa_init();
    for (int i = 0,len = strlen(ST);  i < len ; i++)
    {
        sa_extend(ST[i]-'a');
    }
    topo();
    ms(dp,0);
    for ( int i = 0 ; i < n ; i++)
    {
        scanf("%s",ST);
        int now = rt,len = 0;
        for ( int i = 0,_len = strlen(ST) ; i < _len ; i++)
        {
        if (st[now].nxt[ST[i]-'a']!=-1)
        {
            now = st[now].nxt[ST[i]-'a'];
            len++;
            dp[now] = max(dp[now],len);
          //  printf("now:%d dp[now]:%d len: %d\n",now,dp[now],len);
        }
        else 
        {
            while (now!=-1&&st[now].nxt[ST[i]-'a']==-1) now = st[now].link;
            if (now==-1) now=rt,len=0;else len = st[now].len + 1,now=st[now].nxt[ST[i]-'a'],dp[now] = max(dp[now],len);
        }
        }
    }
    LL ans = 0 ;
    for ( int i = sz ; i >= 1 ; i-- )
    {
        int v = rk[i];
        if (dp[v])
        {
        dp[st[v].link] = max(dp[st[v].link],dp[v]);
        if (dp[v]<st[v].len) ans += st[v].len-dp[v]; // 长度为[dp[v]+1,st[v].len]的串不在n个B串中出现,可以贡献答案。
        }else ans += st[v].len - st[st[v].link].len;
    }
    printf("Case %d: %lld\n",++cas,ans);
    }





#ifndef ONLINE_JUDGE  
    fclose(stdin);
#endif
    return 0;
}

「真诚赞赏,手留余香」

111qqz的小窝

真诚赞赏,手留余香

使用微信扫描二维码完成支付