HDu6583 Typewriter (后缀自动机 + dp)

后缀自动机一个巨强的用法,首先明白有两种转移方式

一、(dp[i] = dp[i - 1] + p)

二、(dp[i] = dp[j] + q) ((j < i))

第一种学过很好处理,主要是第二种的 (j) 如何处理,容易看出,当存在第二种转移时,区间 (s_0-s_j) 一定存在一个字串等于 (s_{j + 1} - s[i]) 为了每次都能快速确认这个 (j) 的位置,我们选择边 (dp) 边处理字符串,当区间 (s_0 - s_j) 存在字串和 (s_{j + 1} - s[i]) 匹配时,我们直接转移,否则我们向后缀自动机添加字符并将 (j = j + 1)

#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <string>
#include <string.h>
#include <map>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn = 5e5 + 50;
const int mod = 20090717;
int INF = 1e9;
struct state {
  int len, link;
  int next[26];
};

string s;
int pos[maxn];
LL dp[maxn];

state st[maxn * 2];
int sz, last;

void next_init(int u){
    for(int i = 0; i < 26; i++) st[u].next[i] = 0;
}
void sam_init() {
  next_init(0);
  st[0].len = 0;
  st[0].link = -1;
  sz = 1;
  last = 0;
}

void sam_extend(int c) {
  int cur = sz++;
  st[cur].len = st[last].len + 1;
  next_init(cur);
  int p = last;
  while (p != -1 && !st[p].next[c]) {
    st[p].next[c] = cur;
    p = st[p].link;
  }
  if (p == -1) {
    st[cur].link = 0;
  } else {
    int q = st[p].next[c];
    if (st[p].len + 1 == st[q].len) {
      st[cur].link = q;
    } else {
      int clone = sz++;
      st[clone].len = st[p].len + 1;
      for(int i = 0; i < 26; i++){
        st[clone].next[i] = st[q].next[i];
      }
      st[clone].link = st[q].link;
      while (p != -1 && st[p].next[c] == q) {
        st[p].next[c] = clone;
        p = st[p].link;
      }
      st[q].link = st[cur].link = clone;
    }
  }
  last = cur;
}

int main(int argc, char const *argv[])
{
    std::ios::sync_with_stdio(false);
    while(cin >> s){
        sam_init();
        int len = s.size();
        LL p, q;
        cin >> p >> q;
        dp[0] = p;
        sam_extend(s[0] - 'a');
        int cur = last;
        int j = 0;
        for(int i = 1; i < len; i++){
            dp[i] = dp[i - 1] + p;
            while(1){
                while(cur != 0 && st[st[cur].link].len + 1 >= i - j){
                    cur = st[cur].link;
                }
                if(st[cur].next[s[i] - 'a']){
                    cur = st[cur].next[s[i] - 'a'];
                    break;
                } else {
                    sam_extend(s[++j] - 'a');
                }   
            }
            dp[i] = min(dp[i], dp[j] + q);
        }
        cout << dp[len - 1] << '
';
    }
    return 0;
}