POJ3267 The Cow Lexicon

知识点:线性dp、字符串

题目

Few know that the cows have their own dictionary with W (1 ≤ W ≤ 600) words, each containing no more 25 of the characters ‘a’…‘z’. Their cowmunication system, based on mooing, is not very accurate; sometimes they hear words that do not make any sense. For instance, Bessie once received a message that said “browndcodw”. As it turns out, the intended message was “browncow” and the two letter "d"s were noise from other parts of the barnyard.

The cows want you to help them decipher a received message (also containing only characters in the range ‘a’…‘z’) of length L (2 ≤ L ≤ 300) characters that is a bit garbled. In particular, they know that the message has some extra letters, and they want you to determine the smallest number of letters that must be removed to make the message a sequence of words from the dictionary.

输入

Line 1: Two space-separated integers, respectively: W and L
Line 2: L characters (followed by a newline, of course): the received message
Lines 3…W+2: The cows’ dictionary, one word per line

输出

Line 1: a single integer that is the smallest number of characters that need to be removed to make the message a sequence of dictionary words.

样例

输入

6 10
browndcodw
cow
milk
white
black
brown
farmer

输出

2

题意

已知单词字典和一段单词与多余字符连接而成的字符串,求最少删除多少多余字符

思路

做过最长子序列的题目可能会认为要求最长子序列,再用总长度减去最长子序列长度,这种方法没有试过,欢迎讨论。
我使用了另一种解法,考虑到字符串是由单词首尾拼接,若设dp[i]为0到i位置最少删除多少字符,发现难以确定0到i位置的字符串中最后一个单词的最后一个字符的位置(注:因为更新dp[i]时i位置不一定是最后一个单词的最后一个字符的位置,所以难以确定),故考虑设dp[i]为i到字符串尾最少删除多少字符,并从右往左更新。因为字符串中横跨i位置的单词对更新dp[i]没有作用,而开始位置(设为i’)大于i的单词已在dp[i’]中更新过,所以若i位置可更新,则一定为单词开始的位置。
从dp[i]推到其中一个子问题dp[j],i到j中只有一个单词,因为如果有两个单词,第二个单词在之前肯定已经更新过了;也可能没有单词,说明i到j要全部删去。所以需要判断字典中是否有在i到j的单词来分类讨论,有则删去单词中不存在的字符,无则全部删去。判断的实现方法使用双指针进行。
提示(可先看代码):
关于对dp[i]=min(dp[pa]+(pa-i)-len,dp[i])的理解,i到pa为包含单词的一段最短连续字符串(即字符串首尾为单词首尾字符),其中存在多余字符,要求删去这些字符的数量,只需字符串长度减去单词长度,即(pa-i)-len。dp[pa]为子问题,pa到m的最少删除数量。
实现思路见代码。

代码

//克服WA难,终得AC
#include"cstdio"
#include"algorithm"
#include"cstring"
#define ll long long
#define rep(i,a,b) for(ll i=a;i<b;i++)
#define rev(i,a,b) for(ll i=a-1;i>=b;i--)
#define clr(arr,val) memset(arr,val,sizeof arr)
#define ios ios::sync_with_wtdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int maxn=6e2+10,maxm=30,maxl=310;
char dic[maxn][maxm];//字典树组
char str[maxl];//字符串
ll dp[maxl];
ll n,m;//n个单词,字符串长度为m

int main() {
   
  scanf("%lld%lld%s",&n,&m,str);
  rep(i,0,n) scanf("%s",dic[i]);
  rev(i,m,0) dp[i]=dp[i+1]+1;//初始化为全部删除
  rev(i,m,0) {
   
  	//更新dp[i]
    rep(j,0,n) {
   
      //匹配字典中j位置单词在字符串i到m中是否匹配,pa:字符串指针,pb:单词指针
      ll pa=i,pb=0;
      ll len=strlen(dic[j]);
      while(pa<m&&pb<len) {
   
        if(str[pa]==dic[j][pb]) {
   
          pa++;
          pb++;
        } else pa++;
      }
      //注意不要写成pa!=m,当字符串最后一个字符恰好是单词最后一个字符的时候pa==m且pb==len,此时仍然算匹配成功。
      if(pb==len) dp[i]=min(dp[pa]+(pa-i)-len,dp[i]);//匹配成功,提示见思路
      else dp[i]=min(dp[i+1]+1,dp[i]);//应该可以是dp[i]=min(dp[pa]+(pa-i),dp[i]),即删除所有字符,但是取最小值后可以简化,没试过,欢迎讨论
    }
  }
  printf("%lld\n",dp[0]);
  return 0;
}
全部评论

相关推荐

11-03 14:38
重庆大学 Java
AAA求offer教程:我手都抬起来了又揣裤兜了
点赞 评论 收藏
分享
喜欢走神的孤勇者练习时长两年半:池是池,发是发,我曾池,我现黑
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务