“将A串变为B串”类动态规划题(已修改解释)

来源:互联网 发布:服装et软件cad 编辑:程序博客网 时间:2024/05/18 13:42

今天考试感觉要被虐爆了

就我的感觉来说,后两题绝对是省队水平 Orz Owen第四题考场50‘

不过成绩也不是很差

得益于AC了第一题吧

先给出一道很久以前刷的题目

字符串是数据结构和计算机语言里很重要的数据类型,在计算机语言中,对于字符串我们有很多的操作定义,因此我们可以对字符串进行很多复杂的运算和操作。实际上,所有复杂的字符串操作都是由字符串的基本操作组成。例如,把子串 a 替换为子串 b,就是用查找、删除和插入这三个基本操作实现的。因此,在复杂字符串操作的编程中,为了提高程序中字符操作的速度,我们就应该用最少的基本操作完成复杂操作。 在这里,假设字符串的基本操作仅为:删除一个字符、插入一个字符和将一个字符修改成另一个字符这三种操作。 我们把进行了一次上述三种操作的任意一种操作称为进行了一步字符基本操作。 下面我们定义两个字符串的编辑距离:对于两个字符串 a 和 b,通过上述的基本操作,我们可以把 a 变成 b 或 b 变成 a;那么,把字符串 a 变成字符串 b 需要的最少基本字符操作步数称为字符串 a 和字符串 b 的编辑距离。 例如,如 a=“ABC”,b=“CBCD”,则 a 与 b 的编辑距离为 2。 你的任务就是:编一个最快的程序来计算任意两个字符串的编辑距离。  输入数据: 第 1 行为字符串 a;第 2 行为字符串 b。注:字符串的长度不大于 1000,字符串中的字符全为大写字母。  输出数据: 编辑距离。  样例 输入文件名:edit.in ABC  CBCD  输出文件名:edit.out 2 


f[i][j]表示第一个串0-i、第二个串0-j之间的编辑距离,按照删除、插入、替换转移即可

code如下

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;const int maxn = 1000 + 5;int len1, len2;int f[maxn][maxn];char s1[maxn], s2[maxn];void init(),work();int Min(int a,int b,int c,int d) {int mix = (a) < (b) ? (a) : (b);mix = (mix) < (c) ? (mix) : (c);   mix = (mix) < (d) ? (mix) : (d);return mix;}int main(){   freopen("edit.in","r",stdin);   freopen("edit.out","w",stdout);      init();   work();      return 0;}void init(){   scanf("%s\n",s1); scanf("%s\n",s2);   len1 = (int) strlen(s1); len2 = (int) strlen(s2);   memset(f, 127, sizeof(f));   if (s1[0] == s2[0])    {      f[0][0] = 0;      for (int i = 1; i <= len1 - 1; ++i) f[i][0] = i;      for (int j = 1; j <= len2 - 1; ++j) f[0][j] = j;   }   else    {      f[0][0] = 1;      if (s1[1] == s2[0]) f[1][0] = 1; else f[1][0] = 2;      if (s1[0] == s2[1]) f[0][1] = 1; else f[0][1] = 2;   }}void work(){   for (int i = 1;i <= len1 - 1; ++i)      for (int j = 1;j <= len2 - 1; ++j)   {   if (s1[i] == s2[j]) f[i][j] = Min(f[i][j], f[i - 1][j] + 1,f[i][j - 1] + 1, f[i - 1][j - 1]);   else if (s1[i]!=s2[j]) f[i][j] = Min(f[i][j], f[i - 1][j] + 1, f[i][j - 1] + 1,f[i - 1][j - 1] + 1);   }cout << f[len1 - 1][len2 - 1];}


然后在说今天考试的第一题

与编辑距离差别不大,A的人少其实我也很纳闷

第一题:盾盾的打字机(drdrd)  【题目描述】 盾盾有一个非常有意思的打字机,现在盾哥要用这台打字机来打出一段文章。 由于有了上次的经验,盾盾预先准备好了一段模板 A 存在了内存中,并以此为基础来打出文章 B。盾盾每次操作可以将内存中的某一个字符改成另一个字符,或者在某一个位置插入一个字符,或者删除某一个位置上的字符。另外,为了避免自己预存的模板太腿反而浪费时间,盾哥在所有操作之前会斟酌一下选择留下模板 A 的某一个最优的子串以保证操作次数尽量少(当然盾盾也可以全保留或一个都不留),这一步不计入操作次数。 试求盾盾要打出文章 B 的最少操作次数。 子串是指母串中连续的一段。  【输入数据】 输入包含多组数据。 对于每组数据,两行,分别表示 A 和 B。  【输出数据】 每组数据一行,一个数,表示最少操作次数。  【输入样例】 aaaaa aaa abcabc bcd abcdef klmnopq  【输出样例】 0 1 7  【数据约定】 30% : |A|, |B| <= 10 100% : 1 <= |A|, |B| <= 1000, 数据组数 <= 10, 输入的串中只包含小写字母 

code

#include <cstdio>#include <string>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;#define str stringconst int maxlen = 1000 + 5;str st1, st2;int tot, f[maxlen][maxlen];int Min(int a, int b) { return a < b ? a : b; }int main(){   freopen("drdrd.in", "r", stdin);   freopen("drdrd.out", "w", stdout);   while (cin >> st1 >> st2)   {      memset(f, 127, sizeof(f));      int len1 = (int) st1.size(), len2 = (int) st2.size();      for (int i = 0; i <= len1; ++i)//注意检查边界条件      {         f[i][0] = 0;         for (int j = 0; j <= len2; ++j)            f[i + 1][j + 1] = Min(f[i + 1][j + 1], f[i][j] + (st1[i] != st2[j])),            i < len1 ? f[i + 1][j] = Min(f[i + 1][j], f[i][j] + 1) : 0,            j < len2 ? f[i][j + 1] = Min(f[i][j + 1], f[i][j] + 1) : 0;      }      tot = INT_MAX;      for (int i = 0; i <= len1; ++i)         tot = Min(tot, f[i][len2]);      cout << tot << endl;   }   return 0;}

但是此题与编辑距离在输出结果时不同,取了min值

为什么呢?

“盾哥在所有操作之前会斟酌一下选择留下模板 A 的某一个最优的子串以保证操作次数尽量少(当然盾盾也可以全保留或一个都不留),这一步不计入操作次数”

即在我们做出所有操作之前(最开始)我们可以先处理A

且因为有“子串是指母串连续的一段”,即我们对第一个串只能从两头开始删(而不是从中间删)

所以有 

for (int i = 0; i <= len1; ++i)

   tot = Min(tot, f[i][len2]);

即处理了仅从A串的 1-i 变为B串,A串后全默认删除并找到min值

但似乎枚举也只处理了删除A串后面某段的情况而没有处理一开始就删除A串前面的某段

而实际上,初值f[i][0]=0,即默认A串前i个已经被删除才继续转移的

理解这点后,此题至此完美解决。

原创粉丝点击