国庆集训Day1T1撰写博客
题目
http://noip.ybtoj.com.cn/contest/584/problem/1
给定长度为\(n\)的字符串\(s\)和\(m\)个字符串\(t\),将\(s\)中的第\(i\)个字符替换成空格的代价为\(a_i\),求使\(s\)中不出现任何一个\(t\)的最小代价.
思路
设\(f_i\)表示处理完\(s\)的前\(i\)个字符的最小代价.
则有\(f_0=0\).
转移(\(pre_i\)表示存在字符串\(t\) ,\(t\)和 \(s\)的字串\(s_{pre_i\ldots i}\)相等,且\(pre_i\)最小,若不存在,\(pre_i=0\)):
memset(f, 0x3f, sizeof(f));
f[0] = 0;
for (int i = 1; i <= n; i++) {
int boundary = pre[i];
for (int j = i; j >= boundary; j--) {
boundary = max(boundary, pre[j]);
f[i] = min(f[i], f[j - 1] + val[j]);
}
}
cout << f[n];
注意到对于每一个\(i\),最终\(j\)的下届(\(boundary\))都是确定的,且是递增的,且状态转移方程中每一项都只和\(i,j\)中的一个有关,符合单调队列优化条件.
故单调队列优化后可以做到\(O(n)\).
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef unsigned long long ull;
const int N = 2e5 + 10 , M = 15;
const ull mod1 = 1e9 + 7 , base1 = 1331;
const ull mod2 = 1e6 + 7 , base2 = 131;
int read() {
int re = 0;
char c = getchar();
while(c < '0' || c > '9')c = getchar();
while(c >= '0' && c <= '9')re = (re << 1) + (re << 3) + c - '0' , c =getchar();
return re;
}
char readc() {
char c = getchar();
while(c < 'a' || c > 'z')c = getchar();
return c;
}
int n , m;
int val[N];
int pre[N];
int f[N];
struct MQueue {
int head , tail;
int j[N] , f[N];
void pop(int boundary) {
while(head < tail && j[head] < boundary)++head;
}
void push(int j_ , int f_) {
while(head < tail && f[tail - 1] >= f_)--tail;
j[tail] = j_ , f[tail] = f_;
++tail;
}
int front() {
return head == tail ? 1e9 : f[head];
}
} mQue;
namespace input {
char s[N] , t[15][N];
int len[15];
template <ull base , ull mod>
ull GetValue (int l , int r) {
static ull p[N] , val[N] ;
static bool vis = false;
if(!vis) {
vis = true;
p[0] = 1;
for(int i = 1 ; i <= n ; i++)
p[i] = p[i - 1] * base % mod , val[i] = val[i - 1] * base % mod + s[i] ;
}
return (val[r] - val[l - 1] * p[r - l + 1] % mod + mod) % mod;
}
void input() {
n = read() , m = read();
for(int i = 1 ; i <= n ; i++)s[i] = readc();
for(int i = 1 ; i <= n ; i++)val[i] = read();
for(int i = 1 ; i <= m ; i++) {
ull v1 = 0 , v2 = 0;
t[i][len[i] = 1] = readc();
do
t[i][++len[i]] = getchar();
while(t[i][len[i]] >= 'a' && t[i][len[i]] <= 'z');
--len[i];
for(int j = 1 ; j <= len[i] ; j++)
v1 = (v1 * base1 + t[i][j]) % mod1 , v2 = (v2 * base2 + t[i][j]) % mod2;
for(int j = 1 ; j + len[i] - 1 <= n ; j++)
if(v1 == GetValue<base1 , mod1>(j , j + len[i] - 1) && v2 == GetValue<base2 , mod2>(j , j + len[i] - 1))
pre[j + len[i] - 1] = max(pre[j + len[i] - 1] , j);
}
}
}
int main() {
freopen("wzadx.in" , "r" , stdin);
freopen("wzadx.out" ,"w" , stdout);
input::input();
memset(f , 0x3f , sizeof(f));
f[0] = 0 , mQue.push(0 , f[0]);
int boundary = 0;
for(int i = 1 ; i <= n ; i++) {
boundary = max(pre[i] , boundary);
mQue.push(i , f[i - 1] + val[i]);
mQue.pop(boundary);
f[i] = min(f[i] , mQue.front());
}
cout << f[n];
return 0;
}