zoukankan      html  css  js  c++  java
  • [2019杭电多校第一场][hdu6583]Typewriter(后缀自动机&&dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6583

    大致题意是说可以花费p在字符串后添加一个任意字符,或者花费q在字符串后添加一个当前字符串的子串。问最少花费多少可以得到目标串。

    一开始想到的dp,dp[i]为得到目标串的1-i的最小花费。

    那么dp[i]=min{dp[i-1]+p,dp[j-1]+q},s[j~i]应该为s[1~j-1]的子串。

    我们可以知道dp数组是一个单调递增的数组,用反证法可以证明:dp[i]由dp[i-1]转移就不用说了,如果dp[i]是由dp[j]转移过来且dp[i-1]>dp[i],那么dp[i-1]也可以由dp[j]转移过来,所以不合法。

    因为dp数组是递增的,则dp[i]如果是由第二种方法转移过来,则j应该是最小的。所以求最小的j满足s[j~i]应该为s[1~j-1]的子串。

    dp[i]的第二种转移位置,应该在是dp[i-1]的第二种转移位置j的基础上往后移。

    所以用双指针的位置,一个代表i,一个代表j,每次在后缀自动机上添加第j个位置的字符,如果s[j~i]在后缀自动机上可以匹配,则转移,不然就把j往后移。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<algorithm>
     5 #include<cstring>
     6 #include<string>
     7 #include<queue>
     8 using namespace std;
     9 typedef long long ll;
    10 const ll maxn = 2e5 + 10;
    11 const ll inf = 1e18;
    12 struct SAM {
    13     int cnt, last, now;
    14     int len[maxn * 2], ch[maxn * 2][30], fa[maxn * 2];
    15     void clear() {
    16         for (int i = 0; i <= cnt; i++) {
    17             len[i] = fa[i] = 0;
    18             memset(ch[i], 0, sizeof(ch[i]));
    19         }
    20     }
    21     void insert(int x) {
    22         int np = ++cnt, p = last;
    23         len[np] = len[p] + 1, last = np;
    24         while (p && !ch[p][x])
    25             ch[p][x] = np, p = fa[p];
    26         if (!p)
    27             fa[np] = 1;
    28         else {
    29             int q = ch[p][x];
    30             if (len[q] == len[p] + 1)
    31                 fa[np] = q;
    32             else {
    33                 int nq = ++cnt;
    34                 len[nq] = len[p] + 1;
    35                 memcpy(ch[nq], ch[q], sizeof(ch[nq]));
    36                 fa[nq] = fa[q];
    37                 fa[np] = fa[q] = nq;
    38                 while (p&&ch[p][x] == q)
    39                     ch[p][x] = nq, p = fa[p];
    40             }
    41         }
    42     }
    43     int Match(int x) {
    44         return ch[now][x];
    45     }
    46     void withdraw(int lens) {
    47         while (now&&len[fa[now]] >= lens)now = fa[now];
    48         if (now == 0)now = 1;
    49     }
    50     void Tran(int x, int lens) {
    51         now = ch[now][x];
    52         if (now == 0)now = 1;
    53         withdraw(lens);
    54     }
    55 }SA;
    56 ll dp[maxn];
    57 char s[maxn];
    58 int main() {
    59     while (~scanf("%s", s + 1)) {
    60         ll p, q, len;
    61         memset(dp, 0, sizeof(dp));
    62         scanf("%lld%lld", &p, &q);
    63         len = strlen(s + 1);
    64         SA.cnt = SA.last = SA.now = 1;
    65         SA.insert(s[1] - 'a');
    66         dp[1] = p;
    67         int l = 2, r = 1;
    68         for (int i = 2; i <= len; i++) {
    69             r++;
    70             dp[i] = dp[i - 1] + p;
    71             while ((!SA.Match(s[i] - 'a') || r - l + 1 > (i + 1) / 2) && l <= r) {
    72                 SA.insert(s[l++] - 'a');
    73                 SA.withdraw(r - l);
    74             }
    75             SA.Tran(s[i] - 'a', r - l + 1);
    76             if (l <= r)
    77                 dp[r] = min(dp[r], dp[l - 1] + q);
    78         }
    79         printf("%lld
    ", dp[len]);
    80         SA.clear();
    81     }
    82 
    83 }
    View Code
  • 相关阅读:
    模拟登陆江西理工大学教务系统
    python3爬虫 -----华东交大校园新闻爬取与数据分析
    以selenium模拟登陆12306
    PAT (Basic Level) Practice (中文)1076 Wifi密码 (15 分)
    PAT (Basic Level) Practice (中文)1047 编程团体赛 (20 分)
    PAT (Basic Level) Practice (中文)1029 旧键盘 (20 分)
    PAT (Basic Level) Practice (中文)1016 部分A+B (15 分)
    PAT (Basic Level) Practice (中文)1031 查验身份证 (15 分)
    PAT (Basic Level) Practice (中文)1041 考试座位号 (15 分)
    PAT (Basic Level) Practice (中文)1037 在霍格沃茨找零钱 (20 分)
  • 原文地址:https://www.cnblogs.com/sainsist/p/11307838.html
Copyright © 2011-2022 走看看