题解 P1032 【字串变换】

Lemoning

2018-08-05 17:51:32

Solution

这题看上去简单,但其实从头到尾全是坑......qwq 首先,读入数据就很懵逼了,告诉你最多6组变换,但又没告诉你确切的组数,这就很无奈了,其实你可以直接写一个for (i=1;i<=6;i++)来读入所有的转换方式,最后找到有效数据的组数,就OK了(亲测) ~~~c++ for (i=1;i<=6;i++) { cin>>sa[i]>>sb[i]; } l=6; while (sa[l][0]=='\0') l--; ~~~ ~~(虽然本蒟蒻不是这么读入的。。。)~~ 其次,在你宽搜的时候,像本蒟蒻这样图省事的人都会直接用find函数找子串,但是注意,并不是所有数据中都是只有一个子串的(比如第5个数据点),应此你也可以用for循环进行搜索 最后,就是时间的问题了~~(都是时辰的错)~~,这个问题比较容易解决(对于精通STL的人来说),用MAP判重剪枝就OK了([MAP的使用方法](https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html)) 若果还有疑问,请看本蒟蒻~~香喷喷~~的代码。 ~~~c++ #include<bits/stdc++.h> //万能头文件 using namespace std; string a,b; //字符串A与字符串B string sa[8],sb[8]; //存放6种转换方式 map<string,int> map1; //用map存放已经宽搜过的字符串,用来判重剪枝(否则会超时) int l; //有l种转换方式 queue<string> q; //存放转换出来的字符串 queue<int> bb; //存放当前转换出来的字符串已经使用的步数 int bfs() { int i,j,k,m,n; string s,ss; while (q.empty()==0&&q.front()!=b&&bb.front()<=10) //当还能继续转换且没转换出字符串B且步数也没有超出10步时进行宽搜 { if (map1[q.front()]==1) //剪枝:如果当前字符串已经宽搜过了,就弹出,进入下一次循环. { q.pop(); bb.pop(); continue; } map1[q.front()]=1; //记录下该字符串 for (i=1;i<=l;i++) //循环出每一种转换方式 { s=q.front(); //将S赋值为当前要操作的字符串 while (1) //找出子串sa[i]的所有位置 { m=s.find(sa[i]); //在S里查找子串sa[i]的第一次出现位置 if (m==-1) break; //如果全找出来(找不到)了,就结束循环 ss=q.front(); //将SS赋值为当前要操作的字符串 ss.replace(m,sa[i].size(),sb[i]); //在SS中用子串sb[i]替换掉S里第一次出现的子串sa[i] q.push(ss); //将转换后的SS压入队列 bb.push(bb.front()+1); //将转换后的SS已经使用的步数压入队列 s[m]='~'; //将S里子串sa[i]的第一次出现位置随便换成另一种无关的字符,这样就可以查找到S里子串sa[i]的下一个出现位置 } } q.pop(); //将操作过的字符串弹出队列 bb.pop(); //操作过的字符串已经用过的步数一块弹出 } if (q.empty()==1||bb.front()>10) return -1;//没法再进行宽搜,或者超出步数,就返回-1 else return bb.front(); //否则,就是找到了,便返回最少使用步数 } int main() { int i,j,k,m,n; cin>>a>>b; //读入字符串A与字符串B l=1; while (cin>>sa[l]>>sb[l]) l++; //读入转换方式 l--; //l初始值为1,所以要减1,才能表示转换方式的数量 if (l==0&&a!=b) //若果没有转换方式且A也不等于B,直接输出"NO ANSWER!"(其实这步可以不要) { cout<<"NO ANSWER!"; return 0; } q.push(a); //将字符串A压入队列 bb.push(0); //将初始步数0压入队列 k=bfs(); //宽搜 if (k==-1) //返回-1说明NO ANSWER! { cout<<"NO ANSWER!"; return 0; } cout<<k; //输出最小步数 } ~~~ 蒟蒻第一次发题解,求过qwq。