• HDU 6583 Typewriter(后缀自动机)


    Typewrite

    [Time Limit: 1500 msquad Memory Limit: 262144 kB ]

    题意

    给出一个字符串 (s),现在你需要构造出这个字符串,你每次可以花费 (q) 在末尾加上任意一个字符或者花费 (p) 复制任意一段已经构造出来的子串到末尾,问最少需要花费多少。

    思路

    (dp[i]) 表示构造到第 (i) 位最少花费多少。
    第一种情况,直接花费 (q) 添加在末尾,(dp[r] = dp[r-1]+q)
    第二种情况,花费 (p) 复制一部分到末尾,设 (s[l+1...r]) 是被复制的串,那么此时需要满足 (s[l+1...r] in s[1...l]),则 (dp[r] = dp[l] + p),此时的问题就是如何维护 (l)
    利用后缀自动机可以表示出所有子串的性质,(pos) 表示满足条件的 (s[l+1...r]) 所在后缀自动机上的节点位置。
    每次插入 (s[r]) 时,就是从 (s[l+1...r-1]in s[1...l] implies s[l+1...r] in s[1...l]) 的过程,所以要让 (pos) 节点往 (s[r]) 方向移动。如果能移动的话,直接将 (pos) 移动过去,否则就扩展这个自动机,将 (s[l]) 插入后在尝试能否移动。在这个过程中,不断维护 (pos) 在满足条件范围的节点上。

    1. 一个问题就是 (pos) 何时不能向 (s[r]) 方向移动,这种情况就是 (pos) 节点不存在 (s[r]) 这条边。
    2. 令一个问题就是如何维护 (pos) 在符合条件的范围内,我们知道 (pos) 节点包含了同样性质的长度从 (len[fa[pos]]+1)(len[pos]) 内的子串,我们只要保证这里面最短的子串 (len[fa[pos]]+1) 不要太长,需要可以表示出后面那部分的串。在插入 (s[r]) 之前,此时只到 (r-1),需要保证 (len[fa[pos]]+1 leq (r-1)-(l+1)+1),插入 (s[r]) 后,此时到了 (r),需要保证 (len[fa[pos]]+1 leq r-(l+1)+1)
    #include <map>
    #include <set>
    #include <list>
    #include <ctime>
    #include <cmath>
    #include <stack>
    #include <queue>
    #include <cfloat>
    #include <string>
    #include <vector>
    #include <cstdio>
    #include <bitset>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define  lowbit(x)  x & (-x)
    #define  mes(a, b)  memset(a, b, sizeof a)
    #define  fi         first
    #define  se         second
    #define  pii        pair<int, int>
    #define  INOPEN     freopen("in.txt", "r", stdin)
    #define  OUTOPEN    freopen("out.txt", "w", stdout)
    
    typedef unsigned long long int ull;
    typedef long long int ll;
    const int    maxn = 2e5 + 10;
    const int    maxm = 1e5 + 10;
    const ll     mod  = 1e9 + 7;
    const ll     INF  = 1e18 + 100;
    const int    inf  = 0x3f3f3f3f;
    const double pi   = acos(-1.0);
    const double eps  = 1e-8;
    using namespace std;
    
    int n, m;
    int cas, tol, T;
    
    struct Sam {
    	int node[maxn<<1][27], fa[maxn<<1], step[maxn<<1];
    	int sz, last;
    	int newnode() {
    		mes(node[++sz], 0);
    		fa[sz] = step[sz] = 0;
    		return sz;
    	}
    	void init() {
    		sz = 0;
    		last = newnode();
    	}
    	void insert(int k) {
    		int p = last, np = last = newnode();
    		step[np] = step[p]+1;
    		for(; p&&!node[p][k]; p=fa[p])
    			node[p][k] = np;
    		if(p == 0) {
    			fa[np] = 1;
    		} else {
    			int q = node[p][k];
    			if(step[q] == step[p]+1) {
    				fa[np] = q;
    			} else {
    				int nq = newnode();
    				for(int i=1; i<=26; i++)
    					node[nq][i] = node[q][i];
    				fa[nq] = fa[q];
    				step[nq] = step[p]+1;
    				fa[np] = fa[q] = nq;
    				for(; p&&node[p][k]==q; p=fa[p])
    					node[p][k] = nq;
    			}
    		}
    	}
    } sam;
    char s[maxn];
    ll dp[maxn];
    
    int main() {
    	while(~scanf("%s", s+1)) {
    		sam.init();
    		int len = strlen(s+1);
    		ll p, q;
    		scanf("%lld%lld", &q, &p);
    		ll ans = 0;
    		int l=0, pos=0;
    		dp[0] = 0;
    		for(int r=1; r<=len; r++) {
    			dp[r] = dp[r-1]+q;
    			int k = s[r]-'a'+1;
    			while(!sam.node[pos][k]) {
    				sam.insert(s[++l]-'a'+1);
    				while(pos && sam.step[sam.fa[pos]]>r-l-2)	
    					pos = sam.fa[pos];
    				if(!pos)	pos = 1;
    			}
    			pos = sam.node[pos][k];
    			while(pos && sam.step[sam.fa[pos]]>r-l-1)	
    				pos = sam.fa[pos];
    			if(!pos)	pos = 1;
    			dp[r] = min(dp[r], dp[l]+p);
    		}
    		printf("%lld
    ", dp[len]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    李宏毅强化学习完整笔记!开源项目《LeeDeepRL-Notes》发布
    做开源界的MATLAB,PyMiner 需要更多热爱开源的你加入
    开源翻译公告 2020.10.26:新上线了几个文档
    《线性代数》(同济版)——教科书中的耻辱柱
    Docker之四:容器之间通信
    Docker之二:linux下Docker安装
    Docker之三:Dockerfile镜像
    Docker之一:概念及命令
    idea使用之配置
    二、redis基本命令学习
  • 原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/11253183.html
Copyright © 2020-2023  润新知