字串变换 (2002 年NOIP全国联赛提高组)
一道看似非常水的题
大意 :将一个字串 经过几种变换规则变为给定的另一个子串 ,求最小操作数。
已知有两个字串 A, B 及一组字串变换的规则(至多6个规则):
A1 -> B1
A2 -> B2
规则的含义为:在 A中的子串 A1 可以变换为 B1、A2 可以变换为 B2 …。
例如:A='abcd'B='xyz'
变换规则为:
‘abc’->‘xu’‘ud’->‘y’‘y’->‘yz’
则此时,A 可以经过一系列的变换变为 B,其变换的过程为:
‘abcd’->‘xud’->‘xy’->‘xyz’
共进行了三次变换,使得 A 变换为B。
思路:从前从后双向BFS 不加限制 若递归次数等于10次仍未找到答案时,输出NO ANSWER 。其他的若替换完后 从 前端BFS的串 在从后端BFS出现过,则此时递归次数乘2减一即为答案, 从后端BFS的情况同上。 具体实现,就按照题意来,依次扫描变换规则,进行替换,替换完的字串加入队列。
双向BFS
#include <iostream> #include <cstring> #include <map> #include <cstdio> using namespace std; const int Max = 1000000; string A, B; string s_change [7][2], Front[Max], Back[Max]; int Limit = 1, head_Front = 1, tail_Front = 1, tail_Back = 1, head_Back = 1, tot = 0; map < string , int > Map_Front; map < string , int > Map_Back; void BFS () { tot++; //记录递归次数 string s, s_replace; // s是 变换后的字串 ,s_replace 是当前需变换的字串 int head, tail; head = head_Front; //调整指针 tail = tail_Front; for (int i = head; i <= tail; i++) for (int j = 1; j <= Limit; j++) //变换种数 ,变为不同的替换规则 { int change_Front = 0; while (change_Front != -1) //每个字串中可以替换的字串不止一个 { s_replace = Front [i]; //取出当前需被变换的字串 change_Front = s_replace.find (s_change [j][0], change_Front ); //查找变换规则 ,change_Front是 当前规则 出现的位置 if (change_Front >= 0) { s = s_replace.replace (change_Front, s_change [j][0].length(), s_change [j][1]); //进行替换, s存的是替换完的字串 if (Map_Front [s] == 0) //剪枝 ,若该字串出现过,则不进入队列 { if (Map_Back [s] != 0) //如果能在中间遇到,就直接输出答案 { cout << tot * 2 - 1; return ; } Front [++tail_Front] = s; //加入队列 Map_Front [s] = 1; //标记为出现过 } } if (change_Front != -1) change_Front += s_change[j][1].length(); //寻找下一个字串 } } head_Front = tail + 1; //同上 ,不过是从后面进行BFS head = head_Back; tail = tail_Back; for (int i = head; i <= tail; ++i) for (int j = 1; j <= Limit; ++j) { int change_Back = 0; while (change_Back != -1) { s_replace = Back[i]; change_Back = s_replace.find (s_change[j][1], change_Back); if (change_Back >= 0) { s = s_replace.replace (change_Back, s_change [j][1].length(), s_change[j][0]); if (Map_Back[s] == 0) { if(Map_Front[s] != 0) { cout << tot * 2; return ; } Back[++tail_Back] = s; Map_Back[s] = 1; } } if (change_Back != -1) change_Back = change_Back + s_change [j][0].length(); } } head_Back = tail + 1; if (tot == 10) //以十次为限,若十次后仍未找到,则输出NO ANSWER { cout << "NO ANSWER!"; return; } BFS (); } int main() { ios::sync_with_stdio (false); cin >> A >> B; while (cin >> s_change[Limit][0] >> s_change [Limit][1]) Limit++; Limit--; // 替换的组数 Map_Front [A] = 1; //用 map 记录 Map_Back [B] = 1; Front [1] = A; //加入队列 Back [1] = B; BFS (); return 0; }
注意:此题属于那种看似很水,但是实现有难度的题。。
myj 吊打我Orz,xxy 捆起来打我Orz,myl 文化课上天Orz, lrh 姿势水平敲高Orz, hkd 特别胖Orz%%%,cys 智商感人Orz,syl zz专业Orz,我没有学上, 我们未来一片光明