字串变换
https://www.luogu.com.cn/problem/P1032
前言:
NOIp提高组2002的一道字符串+广搜题,还是有难度的
因为你首先得通过题意分析出这是一道广搜,然后还要在编程中避开所有的坑(手动狗头)
题目简述:
给定初始字符串A和目标串B,以及不多于6个的变换规则
要求求出将A变成B的最少步骤数,如果步骤数在10步以内(包含10步)就输出答案,否则输出“NO ANSWER!”
解题思路:
(1)首先分析一下为什么是广搜题,题意变换一下:
给定初始位置和目标位置,以及不多于6个的移动方向(变换规则),要求求出从初始到目标的最少步数
这不就是明摆的广搜嘛!?
(2)分析出了广搜,那最基础的代码思路也就成型了:
①首先将输入的规则存入两个数组中,一个存变化前一个存变化后
②以A为出发点开始广搜,对于每一个队首字符串,我们开双层循环枚举它的每一个位置,再枚举每一种变换规则
③然后进入处理变换的函数change中,首先判断改变后的字符串是否合法,合法的话就进行改变并返回压入队尾
(3)再来讲一下change函数,判断是否合法主要是两个条件:
①改变后的字符串长度大于队首字符串的长度,不合法
②改变后的字符和规则不相等,不合法
注意:将以上的思路转换为代码之后,提交上去,你就会发现,你要么T了3、5要么RE了3、5,反正只有60pts。所以就有了如下的第四步优化操作:
(4)设置一个map<string,int> 数组,每处理了一个字符串就标记一下(其实就相当于普通广搜标记当前位置是否走过),这样判重以后,你就A掉了这个题
代码Code:
PS:主程序中用/**/括起来的代码是用来检查样例能不能对用的
#include <bits/stdc++.h>
using namespace std;
int n,ans;
string A,B,gz[1001],gzs[1001];
map<string,int> shan; //shan这个map数组用来剪枝判重
struct node {
string word;
int st;
} q[100001];
inline string change(string tmp,int x,int y) { //改变字符串
string sum="";
if(gz[y].length()+x>tmp.length()) return sum; //判断是否合法
for(register int i=0;i<gz[y].length();i++) {
if(tmp[x+i]!=gz[y][i]) return sum;
}
sum=tmp.substr(0,x); //substr,string的函数,下面会补充介绍
sum+=gzs[y];
sum+=tmp.substr(x+gz[y].length());
return sum;
}
inline void bfs(string a) {
int front=1,tail=1;
q[front].word=a;
q[front].st=0;
while(front<=tail) {
for(register int i=0;i<q[front].word.length();i++) { //枚举当前对头字符串的每一个位置
for(register int j=0;j<n;j++) { //再枚举每一种转换手段
string now=change(q[front].word,i,j);
if(now!=""&&shan[now]!=1) { //如果变换后的字符串合法且没有处理过,就入队
tail++;
q[tail].word=now;
q[tail].st=q[front].st+1; //累加步数
shan[now]=1;
if(now==B) { //如果到达了B就输出步数
ans=q[tail].st;
return ;
}
}
}
}
front++;
}
}
int main() {
cin>>A>>B;
/* n=3;
for(register int i=0;i<n;i++) {
cin>>gz[i]>>gzs[i];
}*/
while(cin>>gz[n]>>gzs[n]) n++; //一直输入规则
bfs(A); //以字符串A为起点开始广搜
if(ans>10||ans==0) puts("NO ANSWER!");
else printf("%d",ans);
return 0;
}
补充一下:
substr(pos,len)返回从pos号位开始、长度为len的子串,时间复杂度为O(len)
吐槽一下:直接输出“NO ANSWER!”可以得到20pts....没加判重的BFS一般是40-60pts不等