o_o 当前时间是:

4:02:39 AM

 

CF1714D 题解

CF1714D 题解

description

给定黑色文本 tn 个字符串 s1,s2...sn. 一次操作可以将 t 中与 si 相等的子串涂成红色。 一个位置多次涂色后仍是红色。si 可以使用多次。 求将 t 涂成红色的最小次数,并输出方案。 无解输出-1.

  • |t|100
  • 测试数据组数 100
  • n10
  • |si|10

solution

线性dp。

fi 表示将 t 的前 i 个字符涂成红色的最小次数,则

  • f0=0

  • fi=min{fj}+1,pji

其中 pi 减去以 ti 结尾的能匹配的长度最大的 sk 的长度。

特别地,若不存在这样的 p,则 fi=

答案即为 f|t|

为了输出方案,我们记录每个 fi 是由哪里转移的并且当前放的是哪一个单词。

由于需要检查字符串是否在 {s} 中,用 std::map 匹配字符串,可将匹配复杂度从 O(n|si|) 变为 O(|si|logn). 代码也更简洁。

code

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
string t,s[110];
int f[N],n,pre[N],len[N];
map<string,bool> mp;
map<string,int> num;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
int T;
cin>>T;
while(T--){
memset(pre,0,sizeof pre);
memset(len,0,sizeof len);
mp.clear();
num.clear();
f[0]=0;
cin>>t>>n;
for(int i=1; i<=n; i++) s[i].clear();
for(int i=1; i<=n; i++) cin>>s[i];
for(int i=1; i<=n; i++){
reverse(s[i].begin(),s[i].end()); //倒着存字符串,方便dp的时候匹配
mp[s[i]]=true;
num[s[i]]=i;
}
f[t.size()+1]=INT_MAX/2; //避免+1爆int
for(int i=1; i<=t.size(); i++){
string now;
f[i]=INT_MAX/2;
int minx=t.size()+1; //记录从哪里转移
for(int j=i; j; j--){
now+=t[j-1];
if(f[minx]>f[j-1]){
minx=j-1;
}
if(mp.find(now)!=mp.end()){
if(f[i]>f[minx]+1){
f[i]=f[minx]+1;
pre[i]=minx;
len[i]=now.size();
}
}
}
}
if(f[t.size()]==INT_MAX/2){
cout<<-1<<'\n';
continue;
}
int st=t.size();
cout<<f[t.size()]<<'\n';
while(st){
string q=t.substr(st-len[st],len[st]); //此处记录了每个位置用了多长的字符串来涂色
reverse(q.begin(),q.end());
cout<<num[q]<<' '<<st-len[st]+1<<'\n';
st=pre[st];
}
}
return 0;
}
posted @   zzafanti  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示