题目链接:
一道简单的DP题。
题目意思是给一个长度为n的字符串s,以及一个有m个单词的字典,求最少需要在s中删除多少个字符之后,s能够完全用字典里的单词表示。
我的做法是DP,时间复杂度大约是O(n*m)。
很容易就能想到将问题划分为这样的子问题:dp[i]表示在前0~i个字符组成的子串中最少删除多少个字符后能过完全用字典里的单词表示。这样一来最后的答案就是dp[n-1]。
状态转移方程就是:
dp[i] = dp[i-1] + 1 当没有单词以s[i]结尾屎;
dp[i]=min( dp[i] , dp[j] + i - j - len ) len是单词的长度,遍历每一个以s[i]结尾的单词,并从s[i]开始向前匹配,直到i = -1或者匹配成功,匹配成功的位置是j + 1(在我的代码中用的是k来表示。
AC代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string.h>
#define N 700
using namespace std;
string s,c;
int m,n;
int dp[N];
string *dict;
int main()
{
int i,j,k,p,a;
cin>>m>>n;
cin>>s;
dict=new string[m];
for(i=0;i<m;i++)
{
cin>>c;
dict[i]=c;//字典
}
dp[0]=1;//初始值
for(i=0;i<n;i++)
{
if(i!=0)
dp[i]=dp[i-1]+1;//初始值
for(j=0;j<m;j++)//枚举每一个单词
{
if(i+1>=dict[j].size()&&dict[j][dict[j].size()-1]==s[i])
//结尾与s[i]相同,而且长度不超
{
p=dict[j].size()-1;
k=i;
while(1)//找到一个与单词j相同的子集
{
while((k+1) && s[k]!=dict[j][p]) k--;
if(k<0) break;//不存在与单词j相同的子集
p--;k--;//位置标记移动
if(p<0) break;//找到了
}
if(k++<0&&p>=0) continue;
//不存在,继续遍历,否则进行状态转移
a=dict[j].size();
if(k>0)
dp[i]=min(dp[i],dp[k-1]+(i-k+1)-a);
//转移方程 a为单词j长度
else dp[i]=min(dp[i],i-k+1-a);
}
}
}
cout<<dp[n-1]<<endl;//结果
}