• 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;
    }
    
  • 相关阅读:
    转载 Android Permission中英对照
    常用vi命令——鸟哥的私房菜
    转载 Android拍照、录像、录音代码范例
    转载 Android ListView滚动提示等待框
    Android ViewFlipper装载baseAdapter
    转载 Android解决java.lang.OutOfMemoryError: bitmap size exceeds VM budget
    转载 Android解决通过Intent调用系统拍照程序,返回图片太小的问题
    垂直输出字符串数组
    Yahoo!网站性能最佳体验的34条黄金守则__JavaScript和CSS
    将16进制转换成10进制
  • 原文地址:https://www.cnblogs.com/PCCCCC/p/13453580.html
Copyright © 2020-2023  润新知