已知有两个字串?A,B?及一组字串变换的规则(至多?66?个规则),形如:
规则的含义为:在?A?中的子串?A1??可以变换为?B1?,A2??可以变换为 B2??。
例如:A=abcd,B=xyz,
变换规则为:
则此时,A?可以经过一系列的变换变为?B,其变换的过程为:
共进行了?3?次变换,使得?A?变换为?B。
第一行有两个字符串?A,B。
接下来若干行,每行有两个字符串?Ai?,Bi?,表示一条变换规则。
若在?1010?步(包含?1010?步)以内能将?A?变换为?B,则输出最少的变换步数;否则输出?NO ANSWER!
。
输入 #1复制
abcd xyz abc xu ud y y yz
输出 #1复制
3
对于?100%100%?数据,保证所有字符串长度的上限为?2020。
【题目来源】
NOIP 2002 提高组第二题
解析:
使用双端队列可以极小的减少搜索范围:
注意逆向的时候是从后变到前的。
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include<cstring>
using namespace std;
//双端队列 + map记录已走过的步数
queue<string> q1,q2;
string A,B;
string a1[10],a2[10];
int n = 0;
int expand(queue<string>& q,map<string,int>& m1,map<string,int>& m2,string a1[],string a2[])
{
string t = q.front();
q.pop();
for(int i = 0;i < t.size();i++)
{
for(int j = 0;j < n;j++)
{
if(t.substr(i,a1[j].size()) == a1[j])
{
string s1 = t.substr(0,i) + a2[j] + t.substr(i+a1[j].size()); // 变成下一个字符
if(m1.count(s1)) continue;
if(m2.count(s1)) return m1[t]+1+m2[s1];
m1[s1] = m1[t]+1;
q.push(s1);
}
}
}
return 11;
}
int bfs()
{
map<string,int> m1,m2;
q1.push(A),m1[A] = 0;
q2.push(B),m2[B] = 0;
while(q1.size() && q2.size()) // 有断层
{
int t;// 每次扩展元素少的
if(q1.size() < q2.size()) t = expand(q1,m1,m2,a1,a2);
else t = expand(q2,m2,m1,a2,a1);
if(t <= 10) return t;
}
return 11;
}
int main()
{
cin >> A >> B;
while(cin >> a1[n] >> a2[n]) n++;
// for(int i = 0;i < 3;i++)
// {
// cin >> a1[i] >> a2[i];
// }
// n = 3;
if(A == B)
{
cout << "0";
return 0;
}
int step = bfs();
if(step > 10)
cout <<"NO ANSWER!"<<endl;
else{
cout << step<<endl;
}
return 0;
}