CF#633C Spy Syndrome 2 DP+二分+hash
Spy Syndrome 2
题意
现在对某个英文句子,进行加密:
- 把所有的字母变成小写字母
- 把所有的单词反过来
- 去掉单词之间的空格
比如:Kira is childish and he hates losing
加密为ariksihsidlihcdnaehsetahgnisol
现在给出加密后的句子,以及m个单词(每个单词可以重复使用),输出原来的句子。
思路
使用dp[i]
表示第i个字母作为句子最后一个单词的开头所在的单词编号。
dp
值为0表示不能组成句子。
对于每个单词hash一下,按照每个单词的hash值从小到大排序。
遍历加密后的句子,对于每个i,往前寻找第一个j使得[i,j]
为一个单词(最多1000次,单词长度最大为1000),纪录这个单词的编号。
对于每一个j,使用二分判断所有的单词中是否存在当前的hash值,如果存在那么[i,j]
就是一个合法的单词。
如果dp[j-1]==0
,说明第j-1个字符作为最后一个单词的开头不合法,那么[i,j]
作为一个单词肯定不合法,跳过。
然后DFS输出一下。
这个思路我就是莽一发,竟然没超时685ms
。
正解好像是字典树+dfs,不过字典树还没学。
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
int dp[N];
char s[N];
struct note
{
int id,val;
char s[1010];
bool operator <(const note&a) const
{
return val<a.val;
}
} arr[N],now;
bool cmp(note a,note b)
{
return a.id<b.id;
}
void dfs(int pos)
{
if(pos==0) return ;
int len=strlen(arr[dp[pos]].s);
dfs(pos-len);
for(int i=0; i<len; i++)
printf("%c",arr[dp[pos]].s[i]);
printf(" ");
}
int main()
{
int lens;
scanf("%d%s",&lens,s+1);
int m;
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
arr[i].val=0;
scanf("%s",arr[i].s);
arr[i].id=i;
int lent=strlen(arr[i].s);
for(int j=0; j<lent; j++)//计算当前单词的hash值
{
int tmp=arr[i].s[j]-'a'+1;
if(arr[i].s[j]>='A'&&arr[i].s[j]<='Z')
tmp+=32;
arr[i].val=(arr[i].val*26%mod+tmp)%mod;
}
}
sort(arr+1,arr+1+m);
dp[0]=1;
for(int i=1; i<=lens; i++)
{
int tmp=0;
for(int j=i; j>max(0,i-1000); j--)//最多遍历1000位
{
tmp=(tmp*26%mod+(s[j]-'a'+1))%mod;
if(!dp[j-1]) continue;//如果dp[j-1]不能组成句子,当前位没有继续的必要
now.val=tmp;
int pos=(int)(lower_bound(arr+1,arr+1+m,now)-arr);
if(pos!=m+1&&arr[pos].val==tmp)
{
dp[i]=arr[pos].id;//纪录单词编号
break;
}
}
}
sort(arr+1,arr+1+m,cmp);
dfs(lens);
printf("\n");
return 0;
}
/*
7
ihereht
3
HI
Ho
there
*/