HDU 6583 Typewriter(后缀自动机)

\(f[i]\)表示到\(i\)所需最小花费
假设\(j\)是满足\(s[j,i]\)\(s[1,j-1]\)的子串的最小值,则\(f[i] = min(f[i−1]+p,f[j−1]+q)\) \((\)因为\(f[i]\)是非递减的\()\)

#include <bits/stdc++.h>
#define Cpy(a, b) memcpy(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int maxn = 200010;
const int Maxn = 400010;
char s[maxn];
struct Suffix_Automata
{
    int maxlen[Maxn], trans[Maxn][26], link[Maxn], Size, Last;
    int now;  //now表示前一次匹配的状态  即在s[1,j-1]中包含子串s[j,i-1]的状态
    void init()
    {
        now=1;
        Size = Last = 1;
        link[1] = 0;
        memset(trans[1], 0, sizeof(trans[1]));
    }
    int newnode()
    {
        int node = (++Size);
        link[node] = 0;
        memset(trans[node], 0, sizeof(trans[node]));
        return node;
    }
    void Extend(int id)
    {
        int cur = newnode(), p;
        maxlen[cur] = maxlen[Last] + 1;
        for (p = Last; p && !trans[p][id]; p = link[p])
            trans[p][id] = cur; 
        if (!p) 
            link[cur] = 1;
        else 
        {
            int q = trans[p][id];
            if (maxlen[q] == maxlen[p] + 1) 
                link[cur] = q;
            else  
            {
                int clone = newnode();
                maxlen[clone] = maxlen[p] + 1;
                Cpy(trans[clone], trans[q]);
                link[clone] = link[q];
                for (; p && trans[p][id] == q; p = link[p])
                    trans[p][id] = clone;
                link[cur] = link[q] = clone;
            }
        }
        Last = cur;
    }
    bool match(int id)
    {
        return trans[now][id]==0;
    }
    void withdraw(int len)
    {
        while(now>1 && maxlen[link[now]]>=len)
            now=link[now];
    }
    void Transfer(int id,int len)
    {
        now=trans[now][id];
        if(!now)now=1;
        // if(len==0)  //(之前的now的len) +1后一定在之后的now中,除了len=0的情况
        withdraw(len);
    }

} sam;
ll f[maxn];
int main()
{
    int a,b;
    while(~scanf("%s", s + 1))
    {
        sam.init();
        scanf("%d%d",&a,&b);
        int len = strlen(s + 1);
        f[1]=a;
        sam.Extend(s[1]-'a');
        int j=2;
        for(int i=2;i<=len;i++)
        {   
            f[i]=f[i-1]+a;
            //若上次状态没有该字符的转移 或者 当前子串(s[j,i])长于这个前缀(s[1,j-1]) 并且子串中还有字符
            //说明当前j不满足条件,必须往右移
            while((sam.match(s[i]-'a')||(i-j+1)>i)&&j<=i)
            {
                sam.Extend(s[j++]-'a');
                sam.withdraw(i-j); //s[j,i-1]的长度变短,调整now的位置,这时s[i]还不包括,故长度为i-j
            }
            sam.Transfer(s[i]-'a',i-j+1); //确定长度和状态之后调整 
            if(j<=i)
                f[i]=min(f[i],f[j-1]+b);
        }
        printf("%lld\n",f[len]);
    }
    return 0;
}
posted @ 2020-06-07 14:32  Zeronera  阅读(145)  评论(0)    收藏  举报