“將A串變爲B串”類動態規劃題(已修改解釋)

今天考試感覺要被虐爆了

就我的感覺來說,後兩題絕對是省隊水平 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 string

const 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個已經被刪除才繼續轉移的

理解這點後,此題至此完美解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章