【字符串/广搜】P1032 字串变换
https://www.luogu.com.cn/problem/P1032
洛谷P1032 字串变换 【普及+/提高】 题解
第一个没看题解AC的绿题,太激动了
这是一道结合字符串的广搜题目。这道题有许多小问题需要解决:
0.输入
这道题的输入有点奇怪,不输入规则数量。所以只能当输入等于EOF(可看作0)时结束。至于存储变换规则,我用的是 pair 的动态数组:
vector<pair<string,string> > rule; vector<string> vis; int n; string start,finish; inline void init() { cin>>start>>finish; string tmpx,tmpy; while(cin>>tmpx>>tmpy)//尚未遇到EOF { pair<string,string> p(tmpx,tmpy); rule.push_back(p); } }
1.字符串变换操作
意思是给定一个字符串,将它根据所有规则,转变为所有能过扩展的字符串,将所有结果存储到一个vector里,也就是BFS对状态的扩展。
直接使用char不易维护和操作,这里可以采用string中的find、append和substr函数,可以高效地进行变换操作。
首先,编写一个函数change(string a)返回a能一步变换出的所有字符串,用一个 vector<string> 来存储这些字符串。假设有规则<u,v>表示可以将字符串u变为字符串v。我们考虑先通过find寻找a中每一个子串u出现的位置,然后从这个位置将a分为两半部分,然后再用两半中间加上一个v,就完成了一步变换。
例如,a="abcdefg",<u,v>=<"bcd","kkkk">。现在find函数在a的位置1处发现了"bcd",那么通过substr,可以将a拆成两部分"a"和"efg",然后在中间插入"kkkk",就形成了目标字符串"akkkkefg"。我们可以发现efg的开头位置就是1+4(find返回值+v的长度)。这样就可以据此通过substr(a,5)拆分出"efg”了。
那如果a中有多个子串和u相等呢?比如说"abcdbcdbcdefg"?
那我们就可以使用另一个find(string a,int x):在s中寻找在位置x后的子串a。返回值与find(string a)相同。我们可以先执行一次find,然后将x自增,就会跳转到下一个子串进行搜索了。具体可以见代码:
1 //设s为string 2 //s.find(string a):在s中寻找完整的子串a,返回a第一次出现的位置(第一个字符的前一个位置)的下标。若s中未发现子串a,则返回一个特殊值string::npos。 3 //s.find(string a,int x):在s中寻找在位置x后的子串a。返回值与find(string a)相同。 4 //s.append(string a):将s后接上a。也可以写作s+=a. 5 inline vector<string> change(string a) 6 { 7 vector<string> obj; 8 for(int i=0;i<rule.size();i++)//对于所有变换规则 9 { 10 string from=rule[i].first,to=rule[i].second;//规则的左部和右部 11 int pos=0;//开始位置,注意下标索引从0开始 12 while((pos=a.find(from,pos))!=string::npos)//还没有完全搜索完毕 13 { 14 string temp=a;//暂时存储当前字符串 15 if(pos+from.length()-1>a.length()) break;//规则的左部如果和前半部分拼接起来已经超出了原先的长度,直接跳出 16 string p=a.substr(0,pos),q=a.substr(pos+from.length());//分成两部分,在中间插入 17 a=p.append(to).append(q);//前半部分+替换部分+后半部分 18 obj.push_back(a);//加入答案序列中 19 a=temp;//还原原字符串(即函数参数) 20 pos++;//位置自增(否则find就会在一个位置进行搜索,导致死循环) 21 } 22 } 23 return obj; 24 }
2.BFS函数
和普通的bfs类似,首先我们要对状态的信息进行保存(在本题中,我们要记录状态中的字符串、以及“走”到这一步的步数),然后要记录已经产生过的字符串,扩展状态时不能对已经产生过的状态继续搜索(相当于我们再做“跳马”等棋类简单bfs问题中的vis数组,表示某个点有没有被访问过),避免广搜产生类似于“死循环”的问题。我们可以用两个vector数组,一个是vector<string> vis表示一个字符串是否已出现过。另一个是vector<pair<string,int> > dis。其中pair<string,int> 表示走到string时使用了int步数,也就是bfs中的状态。由于没法直接通过数组访问这两个要素(也可以用map,但是比较复杂),所以我写了两个函数进行访问。
vector<string> vis; inline bool visited(string s){ for(int i=0;i<vis.size();i++){ if(vis[i]==s) return true;//这个字符串已经被访问过了 } return false; } vector<pair<string,int> > dis; inline int getdis(string s){ for(int i=0;i<dis.size();i++){ if(dis[i].first==s) return dis[i].second;//取出状态进行扩展 } return 0; }
剩下的就是普通的bfs了。注意每次要把新装态压入vis和dis数组里。
inline void bfs(string a) { queue<string> q; q.push(a); vis.push_back(a);//初状态 dis.push_back(make_pair(a,0)); while(!q.empty()) { string now=q.front(); q.pop(); if(now==finish&&getdis(now)<=10){ cout<<getdis(now)<<endl;//获得答案 return; } else if(getdis(now)>10) { cout<<"NO ANSWER!"<<endl;//结合广搜的特点,如果一个状态已经超过了题目限制的10步,那么之后的状态只会比它大,更不可能有答案,输出no answer. return; } vector<string> s=change(now);//获取新状态 //cout<<s.size()<<endl; for(int i=0;i<s.size();i++) { if(!visited(s[i])){//没有被访问过,可扩展 vis.push_back(s[i]);//被访问过 dis.push_back(make_pair(s[i],getdis(now)+1));//新状态压入dis数组 q.push(s[i]);//压入队列 } } } cout<<"NO ANSWER!"<<endl;//队空表示无法进行任何扩展,没有答案 }
最终代码:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<map> #define ll long long #define re register using namespace std; vector<pair<string,string> > rule; vector<string> vis; int n; string start,finish; inline void init() { cin>>start>>finish; string tmpx,tmpy; while(cin>>tmpx>>tmpy) { pair<string,string> p(tmpx,tmpy); rule.push_back(p); } } inline vector<string> change(string a) { vector<string> obj; for(int i=0;i<rule.size();i++) { string from=rule[i].first,to=rule[i].second; int pos=0; while((pos=a.find(from,pos))!=string::npos) { string temp=a; if(pos+from.length()-1>a.length()) break; string p=a.substr(0,pos),q=a.substr(pos+from.length()); a=p.append(to).append(q); obj.push_back(a); a=temp; pos++; } } return obj; } inline bool visited(string s){ for(int i=0;i<vis.size();i++){ if(vis[i]==s) return true; } return false; } vector<pair<string,int> > dis; inline int getdis(string s){ for(int i=0;i<dis.size();i++){ if(dis[i].first==s) return dis[i].second; } return 0; } inline void bfs(string a) { queue<string> q; q.push(a); vis.push_back(a); dis.push_back(make_pair(a,0)); while(!q.empty()) { string now=q.front(); q.pop(); if(now==finish&&getdis(now)<=10){ cout<<getdis(now)<<endl; return; } else if(getdis(now)>10) { cout<<"NO ANSWER!"<<endl; return; } vector<string> s=change(now); //cout<<s.size()<<endl; for(int i=0;i<s.size();i++) { if(!visited(s[i])){ vis.push_back(s[i]); dis.push_back(make_pair(s[i],getdis(now)+1)); q.push(s[i]); } } } cout<<"NO ANSWER!"<<endl; } int main() { //freopen("P1032_5.in","r",stdin); init(); vector<string> k=change(start); bfs(start); return 0; }