后缀自动机博客推荐:
https://blog.csdn.net/qq_35649707/article/details/66473069
https://blog.csdn.net/liyuanshuo_nuc/article/details/53561527
T6583
关键:
如何沿着后缀链跑很重要,直接每次暴力的从后缀链往前跑的话,会t(原因是:aaaaaaaaaaaaa)全是一种字符的数据。复杂度高达O(n^2)。所以定义一个now,先从当前值往后匹配看是否有相同的子串,没有则往后缀链跑,有则直接往前匹配。最后就是要记录每个状态终结点不止一个,我们取最靠前的那个(记录在tag[ ] 数组),以免复制的子串出现重复。
#include"bits/stdc++.h"
#define debug(x) cout<<"["<<#x<<" = "<<x<<"]"<<endl;
using namespace std;
typedef long long LL;
const int MAX = 4e5+7;
const int maxn = 4e5+7;
const int N=MAX;
int q[MAX],head,tail;
LL c[MAX],dp[MAX];
char str[MAX];
LL A,B,P,Q;
struct SAM
{
// ch 转移函数, pre父亲, len最长长度, tag前缀标记, in入度, endnum endpos的个数 o 顺序
int ch[maxn][26],pre[maxn],len[maxn],tag[maxn];
int last, tot, now;
void init(){
now = last = tot = 0;
memset(ch[0], -1, sizeof ch[0]);
pre[0] = -1; len[0] = 0; tag[0] = 0;
}
// 加多个串时为了保证不跨串算
void reset() {last = 0;}
int extend(int c, int ind){
int p = last, np = ++tot;
len[np] = len[p] + 1; tag[np] = ind; //添加
memset(ch[np], -1, sizeof ch[np]);
while(~p && ch[p][c] == -1) ch[p][c] = np, p = pre[p];
if(p == -1) {
pre[np] = 0;
}
else{
int q = ch[p][c];
if(len[q] != len[p] + 1){
int nq = ++tot;
memcpy(ch[nq], ch[q], sizeof ch[q]);
len[nq] = len[p] + 1;
pre[nq] = pre[q];
pre[q] = pre[np] = nq;
tag[nq] = min(tag[np],tag[q]); //添加
tag[pre[q]] = min(tag[pre[q]],tag[nq]); //添加
while(~p && ch[p][c] == q) ch[p][c] = nq, p = pre[p];
}
else {
pre[np] = q;
}
}
last = np;
return len[np] - len[pre[np]];
}
void get_next(int c, int k) {
while((~pre[now]) && !ch[now][c]) now = pre[now];
now = ch[now][c];
while((~pre[now]) && tag[now] + len[pre[now]]+1 > k ) now = pre[now];
int j = tag[now] + len[now] > k? tag[now]: k-len[now];
dp[k] = min(dp[k],dp[j]+Q);
}
}sam;
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
while(~scanf("%s",str+1)) {
scanf("%lld%lld",&P,&Q);
sam.init(); head = 1, tail = 0;
int len = strlen(str+1);
for(int i=1; i<=len; i++) {
int cv = str[i] - 'a';
dp[i]=dp[i-1]+P;
sam.extend(cv,i);
sam.get_next(cv,i);
}
printf("%lld\n",dp[len]);
}
return 0;
}
T5470
关键:
因为A和B的关系导致取最靠前的一段复制并不能得到最优解,最靠前往后的值也有可能树答案,但是由于下标 i 的最靠前转移位置一定 <= 下标 i+1 最靠前的转移位置。所以用单调队列优化dp。
#include"bits/stdc++.h"
#define debug(x) cout<<"["<<#x<<" = "<<x<<"]"<<endl;
using namespace std;
typedef long long LL;
const int MAX = 4e5+7;
const int maxn = 4e5+7;
const int N=MAX;
int q[MAX],head,tail;
LL c[MAX],dp[MAX];
char str[MAX];
LL A,B;
struct SAM
{
// ch 转移函数, pre父亲, len最长长度, tag前缀标记, in入度, endnum endpos的个数 o 顺序
int ch[maxn][26],pre[maxn],len[maxn],tag[maxn];
int last, tot, now;
void init(){
now = last = tot = 0;
memset(ch[0], -1, sizeof ch[0]);
pre[0] = -1; len[0] = 0; tag[0] = 0;
}
// 加多个串时为了保证不跨串算
void reset() {last = 0;}
int extend(int c, int ind){
int p = last, np = ++tot;
len[np] = len[p] + 1; tag[np] = ind; //添加
memset(ch[np], -1, sizeof ch[np]);
while(~p && ch[p][c] == -1) ch[p][c] = np, p = pre[p];
if(p == -1) {
pre[np] = 0;
}
else{
int q = ch[p][c];
if(len[q] != len[p] + 1){
int nq = ++tot;
memcpy(ch[nq], ch[q], sizeof ch[q]);
len[nq] = len[p] + 1;
pre[nq] = pre[q];
pre[q] = pre[np] = nq;
tag[nq] = min(tag[np],tag[q]); //添加
tag[pre[q]] = min(tag[pre[q]],tag[nq]); //添加
while(~p && ch[p][c] == q) ch[p][c] = nq, p = pre[p];
}
else {
pre[np] = q;
}
}
last = np;
return len[np] - len[pre[np]];
}
void get_next(int c, int k) {
while((~pre[now]) && !ch[now][c]) now = pre[now];
now = ch[now][c];
while((~pre[now]) && tag[now] + len[pre[now]]+1 > k ) now = pre[now];
int j = tag[now] + len[now] > k? tag[now]: k-len[now];
while(head <= tail && q[head] < j) ++head;
if(head <= tail) dp[k] = min(dp[k],dp[q[head]]+A*(k-q[head])+B+B);
while(head <= tail && dp[k]-A*k <= dp[q[tail]]-A*q[tail]) --tail;
q[++tail]=k;
}
}sam;
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
int T,cas = 0;
scanf("%d",&T);
while(T--) {
scanf("%s",str+1);
for(int i = 0; i < 26; i++) {
scanf("%d",&c[i]);
}
scanf("%lld%lld",&A,&B);
sam.init(); head = 1, tail = 0;
int len = strlen(str+1);
for(int i=1; i<=len; i++) {
int cv = str[i] - 'a';
dp[i]=dp[i-1]+c[cv];
sam.extend(cv,i);
sam.get_next(cv,i);
}
printf("Case #%d: %lld\n",++cas,dp[len]);
}
return 0;
}