给定一个长度为 m 的01串
A , n 个长度为Li 的01串 Bi (总长为 L )。另有一个长度为n 的序列 ci ,表示 Bi 被选中1次的代价。要求将A划分成若干个串,并为每个划分出来的子串找一个 Bki ( ki 可以重复,重复代价算多次),满足此子串是 Bki 的前缀或后缀。求最小总代价,如果不存在这样的划分,输出 −1 。
为了造福群众,把数据放出来吧:https://pan.baidu.com/s/1mi68ZMS
题解:
基本上A了这道题字符串的板都差不多了。。
首先很容易想到
f[i]
表示到A的前
i
为的最小代价。
那么朴素的转移是
考虑将字符串分组,小于 n√ 的建立Trie树,大于的建立后缀数组。
可以考虑先 O(n√) 暴力的在Trie树上DP后缀。对于前缀,则在每个最优状态被更新后用来更新后面的状态(可以分块)。这个最优的 DP 值只用放在最后即可。因为 f[i] 严格单调不降,dp到每一位直接找最后的最小值即可了。
对于那些大于 n√ 的串,对于前缀同样求得最长公共前缀后直接打标记。后缀则相当于查找前面的区间的 RMQ ,这个边 DP 边处理ST表即可实现 O(1) 查询。
算了讲得太辣鸡了还是把题解贴下来吧:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int rd(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
const int N=1e6+50+2e5,S=600;const ll INF=0x3f3f3f3f3f3f3f3f;
int n,m,L,R,len[N],c[N],prep[N],sufp[N],Log[N*2],num[N],tot;
char a[N],b[N];
struct SuffixArray{
int rc,ch[N],len,tp1[N*2],tp2[N*2],*rk,*sa2,sa[N*2],ht[N][21];
SuffixArray(){rk=tp1;sa2=tp2;rc=2;}
inline int ins(char *str){
int l=strlen(str+1);int tp=len+1;
for(int i=1;i<=l;i++)ch[++len]=str[i]-'0';
ch[++len]=++rc;
return tp;
}
inline void Rsort(){
static int cnt[N];
for(int i=1;i<=rc;i++)cnt[i]=0;
for(int i=1;i<=len;i++)cnt[rk[i]]++;
for(int i=1;i<=rc;i++)cnt[i]+=cnt[i-1];
for(int i=len;i>=1;i--)
sa[cnt[rk[sa2[i]]]--]=sa2[i];
}
inline void init(){
for(int i=1;i<=len;i++)rk[i]=ch[i],tp2[i]=i;
Rsort();
for(int p=0,w=1;rc!=len;p=0,w<<=1){
for(int i=len-w+1;i<=len;i++)sa2[++p]=i;
for(int i=1;i<=len;i++)if(sa[i]>w)sa2[++p]=sa[i]-w;
Rsort();swap(rk,sa2);rk[sa[1]]=(p=1);
for(int i=2;i<=len;i++){
int p1=sa[i],p2=sa[i-1];
rk[sa[i]]=(sa2[p1]!=sa2[p2]||sa2[p1+w]!=sa2[p2+w])?(++p):p;
}
rc=p;
}
for(int i=1,k=0;i<=len;i++){
k?k--:0;if(rk[i]==1)continue;
int p1=sa[rk[i]-1];
while(ch[p1+k]==ch[i+k]) ++k;
ht[rk[i]][0]=k;
}
for(int i=1;i<=20;i++){
for(int j=1;j<=len&&(j+(1<<i)-1)<=len;j++){
ht[j][i]=min(ht[j][i-1],ht[j+(1<<(i-1))][i-1]);
}
}
}
inline int getpre(int l,int r){
l=rk[l]; r=rk[r];
if(l>r)swap(l,r);
++l; int t=Log[r-l+1];
return min(ht[l][t],ht[r-(1<<t)+1][t]);
}
}sa;
struct Trie{
int mn[N],son[N][2],cnt;
Trie(){
memset(mn,0x3f,sizeof(mn));
cnt=1;
}
inline void ins(char *str,int v){
int pos=1,l=strlen(str+1);
for(int i=1;i<=l;i++){
int t=str[i]-'0';
if(!son[pos][t])son[pos][t]=++cnt;
pos=son[pos][t];mn[pos]=min(mn[pos],v);
}
}
}trie_pre,trie_suf;
struct Block{
ll mn[S+50],v[S+50][S+50];
Block(){
memset(mn,0x3f,sizeof(mn));
memset(v,0x3f,sizeof(v));
}
inline void upt(int pos,ll val){
int p=(pos-1)/S+1;
mn[p]=min(mn[p],val);
v[p][(pos-1)%S+1]=min(v[p][(pos-1)%S+1],val);
}
inline ll querymn(int pos){
int t=(pos-1)/S+1;
ll rs=INF;
for(int i=pos;i<=m&&(i%S!=1);++i){
rs=min(rs,v[t][(i-1)%S+1]);
}
if(pos%S!=1)++t;
while(t<=R)rs=min(rs,mn[t++]);
return rs;
}
}bl;
struct SparseTable{
ll dp[N][21];
SparseTable(){
memset(dp,0x3f,sizeof(dp));
}
inline ll getmn(int l,int r){
int t=Log[r-l+1];
return min(dp[l][t],dp[r-(1<<t)+1][t]);
}
inline void upt(int p){
int pos=1;ll rs=INF;
for(int i=p;i>=1&&i>i-S;i--){
int t=a[i]-'0';
if(!trie_suf.son[pos][t])break;
pos=trie_suf.son[pos][t];
rs=min(rs,dp[i-1][0]+trie_suf.mn[pos]);
}
for(int i=1;i<=tot;i++){
int l=sa.getpre(2*m-p+2,sufp[i]);
if(l>p) l=p;
if(l) rs=min(rs,getmn(p-l,p-1)+c[i]);
}
dp[p][0]=min(rs,bl.querymn(p));
for(int i=1;i<=20;i++){
int t=p-(1<<i)+1;
if(t<0)break;
dp[t][i]=min(dp[t][i-1],dp[t+(1<<(i-1))][i-1]);
}
}
}st;
inline void update_pre(int p){
int pos=1;
for(int i=p+1;i<=m&&i<=p+S;i++){
int t=a[i]-'0';
if(!trie_pre.son[pos][t])break;
pos=trie_pre.son[pos][t];
bl.upt(i,st.dp[p][0]+trie_pre.mn[pos]);
}
for(int i=1;i<=tot;i++){
int l=sa.getpre(p+1,prep[num[i]]);
if(l>m-p) l=m-p;
if(l) bl.upt(p+l,st.dp[p][0]+c[num[i]]);
}
}
int main(){
scanf("%d%d%d",&m,&n,&L); Log[1]=0; R=(m-1)/S+1;
scanf("%s",a+1);
sa.ins(a);
reverse(a+1,a+m+1);
sa.ins(a);
reverse(a+1,a+m+1);
for(int i=1;i<=n;i++){
scanf("%d%s",&c[i],b+1);
int len=strlen(b+1);
if(len<=S)trie_pre.ins(b,c[i]);else num[++tot]=i, prep[i]=sa.ins(b);
reverse(b+1,b+len+1);
if(len<=S)trie_suf.ins(b,c[i]);else sufp[i]=sa.ins(b);
}
sa.init();
for(int i=2;i<=sa.len;i++) Log[i]=Log[i>>1]+1;
st.dp[0][0]=0;
update_pre(0);
for(int i=1;i<=m;i++){
st.upt(i);
update_pre(i);
}
cout<<(st.dp[m][0]>=INF?-1:st.dp[m][0])<<endl;
}