NOIP 2002 提高组 字串变换
题目描述
已知有两个字串 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。
输入输出格式
输入格式:
键盘输人文件名。文件格式如下:
A B A1 B1 \
A2 B2 |-> 变换规则
... ... /
所有字符串长度的上限为 20。
输出格式:
输出至屏幕。格式如下:
若在 10 步(包含 10步)以内能将 A 变换为 B ,则输出最少的变换步数;否则输出"NO ANSWER!"
输入输出样例
abcd xyz
abc xu
ud y
y yz
3
表示做题时感到了NOIP深深的恶意……
这道题并不容易用比较快的数据结构实现,比如我的字符数组强迫症和模拟队列强迫症就被压得很惨,直接被教做人
好在数据并不大,最多求十步,反复提交发现只有不到十种变换方式,所以还是可以用string和STL que的,如果数据大,强迫症真得被搞死
所以如果可以用string和STL que的话,这题还比较好解
主要思想是如果当前能够变换就进行变换,然后把变换过的放入队尾,什么时候对头是变换完成的就直接输出步数
但是会超时,所以需要剪枝,可能有的串先变换与后变换变出的串相同,所以一个串只需记录第一次出现的情况,后来的步数肯定比第一次的步数多,所以不要,可以用一个map实现,存储<string,bool>如果串出现了,就将bool变为1,蛮巧妙地就将重复的情况解决了
而替换字符串是重点,这就是为什么不用字符数组,因为字符串有函数,实在是好使,大杀器一样,这个函数叫做 .replace(a,len,b),有三个参数,大概用法就是将字符串a后的len位替换为字符串b,与字符串b的大小无关,就是说b多长都能塞到len大小的空当里
上代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<queue> 5 #include<map> 6 using namespace std; 7 int way=1; 8 string start,end,a[11],b[11]; 9 struct data{ 10 string s; 11 int step; 12 }aa; 13 queue<data> que;//队列记录当前情况字符串以及步数 14 map<string,bool> m; 15 int main(){ 16 cin>>start>>end; 17 while(cin>>a[way]>>b[way]) 18 way++; 19 way--;//变换种类 20 aa.s=start; 21 aa.step=0; 22 que.push(aa); 23 m[start]=1; 24 while(!que.empty()){ 25 data temp=que.front(); 26 que.pop(); 27 if(temp.step>10){ 28 printf("NO ANSWER!");//超步输出然后直接break 29 return 0; 30 } 31 if(temp.s==end){ 32 printf("%d",temp.step);//如果是最终情况就输出当前步数 33 return 0; 34 } 35 string t=temp.s; 36 int len=t.length(); 37 string next; 38 for(int i=1;i<=way;i++){//将每一种变换方式都尝试并入队 39 int llen=a[i].length(); 40 for(int j=0;j<len;j++){ 41 if(t.substr(j,llen)==a[i]){ 42 next=t; 43 next.replace(j,llen,b[i]); 44 if(!m[next]){//看看是否出现过 45 m[next]=1; 46 data tt; 47 tt.step=temp.step+1; 48 tt.s=next; 49 que.push(tt);//如果都能满足就入队 50 } 51 } 52 } 53 } 54 } 55 return 0; 56 }