洛谷有个题目弱化版 P1032 字串变换
原串st变到目标串ed,最少的步数,用bfs求解
需要一个map记录某个串是不是被搜到过,如果已经搜过了就不再继续搜 。
对于我们当前从队列头部取出来的字符串,我们依次枚举它的位置i (0<=i<len),然后在枚举所有变换手段
如果当前从队列里取出的字符串的i位置满足我们的枚举的变换手段,既当前字符串从i位置开始的若干个字符满足我们枚举的变换手段,那我们就把这若干个字符替换掉,并把得到的新串加入队列
这里先贴一个代码
#include <bits/stdc++.h>
using namespace std;
string st, ed;
string ss[7], e[7];
map<string, bool> mp;
struct rec{
string s;
int cnt;
rec(string _s, int _cnt) :s(_s), cnt(_cnt){};
};
int tot;
int work(string &now,string s,int idx,int t)
{
if (idx + ss[t].size() > s.size()) return 0; //超过长度不满足
for (int i = 0; i < ss[t].size(); i++) if (s[idx + i] != ss[t][i]) return 0;//不匹配不满足
now=s.substr(0,idx);
now += e[t];
now += s.substr(idx+ss[t].size());
return 1;
}
void bfs()
{
queue<rec> que;
que.push(rec(st, 0));
while (!que.empty())
{
rec cur = que.front();
que.pop();
if (mp.find(cur.s) != mp.end()) continue; //判重
if (cur.cnt > 10) {
puts("NO ANSWER!");
return ;
}
if (cur.s == ed) {
cout << cur.cnt << endl;
return;
}
mp[cur.s] = 1;
for (int i = 0; i < cur.s.size(); i++)//枚举位置i
{
for (int j = 0; j < tot; j++)//枚举变换手段
{
string now = "";
if (work(now, cur.s, i, j)) que.push(rec(now, cur.cnt + 1));
}
}
}
puts("NO ANSWER!");
return;
}
void solve()
{
if (st == ed) cout << 0 << endl;
else bfs();
}
int main()
{
cin >> st >> ed;
while (cin >> ss[tot] >> e[tot]) tot++;
solve();
return 0;
}
刚才我们枚举当前字符串串的位置i,再枚举变换手段其实就是一个模式串与文本串匹配的过程,我们可以用kmp求解。
当前的字符串看作文本串,枚举的变换手段看作模式串,然后去匹配,匹配成功就把新串加入队列。
#include <bits/stdc++.h>
using namespace std;
string st, ed;
string ss[7], e[7];
const int maxn = 1e5 + 5;
int Next[7][maxn];
map<string, bool> mp;
struct rec{
string s;
int cnt;
rec(string _s, int _cnt) :s(_s), cnt(_cnt){};
};
int tot;
void get_next(int x)//预处理next数组
{
int len = ss[x].size();
Next[x][0] = -1;
int i = 0, j = -1;
while (i<len)
{
if (j == -1 || ss[x][i] == ss[x][j])
{
i++, j++;
Next[x][i] = j;
}
else
j = Next[x][j];
}
}
void kmp(queue<rec> &Q,int x,string s,int cnt)
{
int len2= ss[x].size(),len1=s.size();
int i = 0, j = 0;
while (i < len1&&j < len2)
{
if (j == -1 || s[i] == ss[x][j])
{
i++, j++;
if (j == len2)//找到了
{
int idx = i - j;
string now = "";
now = s.substr(0, idx);
now += e[x];
now += s.substr(idx + ss[x].size());
Q.push(rec(now, cnt + 1));
j = Next[x][j];//找到一个匹配的还要继续寻找,
}
}
else
j = Next[x][j];
}
}
void bfs()
{
queue<rec> que;
que.push(rec(st, 0));
while (!que.empty())
{
rec cur = que.front();
que.pop();
if (mp.find(cur.s) != mp.end()) continue; //判重
if (cur.cnt > 10) {
puts("NO ANSWER!");
return;
}
if (cur.s == ed) {
cout << cur.cnt << endl;
return;
}
mp[cur.s] = 1;
for (int i = 0; i < tot; i++)//枚举变换手段,ss[i]可以看作模式串,cur.s看作文本串
kmp(que,i,cur.s,cur.cnt);
}
puts("NO ANSWER!");
return;
}
void solve()
{
if (st == ed) cout << 0 << endl;
else bfs();
}
int main()
{
cin >> st >> ed;
while (cin >> ss[tot] >> e[tot]) get_next(tot),tot++;
solve();
return 0;
}
实际上两个算法差不多,kmp稍微快上一点,差距不大,但是都过不了ch2908 的。接下来在来一个双向bfs
双向bfs就是一边从从起点往终点搜,一边从终点往起点搜,这样减少了搜索树的分支。
我用2个map分别保存两个方向搜索到每个字符串所需最少的步数,这样从起点往终点搜的过程中检查另一个map中是否有这个字符串,有就输出步数,终点往起点搜也是一样
同时map起到标记的作用。
这是我比较喜欢的搜索顺序
#include <bits/stdc++.h>
using namespace std;
string st, ed;
string ss[7], e[7];
map<string, int> mp1, mp2;
struct rec{
string s;
int cnt;
rec(string _s, int _cnt) :s(_s), cnt(_cnt){};
};
int tot;
int work(string &now, string s, int idx, int t)
{
if (idx + ss[t].size() > s.size()) return 0;
for (int i = 0; i < ss[t].size(); i++) if (s[idx + i] != ss[t][i]) return 0;
now = s.substr(0, idx);
now += e[t];
now += s.substr(idx + ss[t].size());
return 1;
}
int work1(string &now, string s, int idx, int t)
{
if (idx + e[t].size() > s.size()) return 0;
for (int i = 0; i < e[t].size(); i++) if (s[idx + i] != e[t][i]) return 0;
now = s.substr(0, idx);
now += ss[t];
now += s.substr(idx + e[t].size());
return 1;
}
void bfs()
{
queue<rec> q1, q2;
q1.push(rec(st, 0));
q2.push(rec(ed, 0));
while (!q1.empty() && !q2.empty())
{
rec cur1 = q1.front();
q1.pop();
rec cur2 = q2.front();
q2.pop();
if (cur1.cnt + cur2.cnt > 10) {
puts("NO ANSWER!");
return;
}
if (mp1.find(cur2.s) != mp1.end() && mp1.find(cur1.s) == mp1.end())
{
cout << min(mp1[cur2.s] + cur2.cnt, mp2[cur1.s] + cur1.cnt) << endl;
return;
}
else if (mp1.find(cur2.s) != mp1.end())
{
cout << mp1[cur2.s] + cur2.cnt << endl;
return;
}
else if (mp2.find(cur1.s) != mp2.end())
{
cout << mp2[cur1.s] + cur1.cnt << endl;
return;
}
//从起点往终点搜索
for (int i = 0; i < cur1.s.size(); i++)
{
for (int j = 0; j < tot; j++)
{
string now = "";
if (work(now, cur1.s, i, j) && mp1.find(now) == mp1.end()) {//map标记
q1.push(rec(now, cur1.cnt + 1));
mp1[now] = cur1.cnt + 1;
}
}
}
//从终点往起点搜索
for (int i = 0; i < cur2.s.size(); i++)
{
for (int j = 0; j < tot; j++)
{
string now = "";
if (work1(now, cur2.s, i, j) && mp2.find(now) == mp2.end()){
q2.push(rec(now, cur2.cnt + 1));
mp2[now] = cur2.cnt + 1;
}
}
}
}
puts("NO ANSWER!");
return;
}
void solve()
{
if (st == ed) cout << 0 << endl;
else bfs();
}
int main()
{
cin >> st >> ed;
while (cin >> ss[tot] >> e[tot]) tot++;
solve();
return 0;
}
这一个改变了一下mp判断的位置,我还是比较喜欢上一个map判断的位置
#include <bits/stdc++.h>
using namespace std;
string st, ed;
string ss[7], e[7];
map<string, int> mp1, mp2;
struct rec{
string s;
int cnt;
rec(string _s, int _cnt) :s(_s), cnt(_cnt){};
};
int tot;
int work(string &now, string s, int idx, int t)
{
if (idx + ss[t].size() > s.size()) return 0;
for (int i = 0; i < ss[t].size(); i++) if (s[idx + i] != ss[t][i]) return 0;
now = s.substr(0, idx);
now += e[t];
now += s.substr(idx + ss[t].size());
return 1;
}
int work1(string &now, string s, int idx, int t)
{
if (idx + e[t].size() > s.size()) return 0;
for (int i = 0; i < e[t].size(); i++) if (s[idx + i] != e[t][i]) return 0;
now = s.substr(0, idx);
now += ss[t];
now += s.substr(idx + e[t].size());
return 1;
}
void bfs()
{
queue<rec> q1, q2;
q1.push(rec(st, 0));
q2.push(rec(ed, 0));
while (!q1.empty() && !q2.empty())
{
rec cur1 = q1.front();
q1.pop();
rec cur2 = q2.front();
q2.pop();
if (cur1.cnt + cur2.cnt > 10) {
puts("NO ANSWER!");
return;
}
if(mp1.find(cur2.s) != mp1.end()&&mp1.find(cur1.s) == mp1.end())
{
cout<<min(mp1[cur2.s] + cur2.cnt,mp2[cur1.s] + cur1.cnt )<<endl;
return;
}
else if (mp1.find(cur2.s) != mp1.end())
{
cout << mp1[cur2.s] + cur2.cnt << endl;
return;
}
else if (mp2.find(cur1.s) != mp2.end())
{
cout << mp2[cur1.s] + cur1.cnt << endl;
return;
}
if (mp1.find(cur1.s) == mp1.end())//没出现过
{
mp1[cur1.s] = cur1.cnt;
/*
也可以在这里查找
if(mp1.find(cur2.s) != mp1.end())
{
cout << mp1[cur2.s] + cur2.cnt << endl;
return;
}
*/
for (int i = 0; i < cur1.s.size(); i++)
{
for (int j = 0; j < tot; j++)
{
string now = "";
if (work(now, cur1.s, i, j)) {
q1.push(rec(now, cur1.cnt + 1));
}
}
}
}
if (mp2.find(cur2.s) == mp2.end())
{
mp2[cur2.s] = cur2.cnt;
/*
也可以在这里查找
if (mp2.find(cur1.s) != mp2.end())
{
cout << mp2[cur1.s] + cur1.cnt << endl;
return;
}
*/
for (int i = 0; i < cur2.s.size(); i++)
{
for (int j = 0; j < tot; j++)
{
string now = "";
if (work1(now, cur2.s, i, j)){
q2.push(rec(now, cur2.cnt + 1));
}
}
}
}
}
puts("NO ANSWER!");
return;
}
void solve()
{
if (st == ed) cout << 0 << endl;
else bfs();
}
int main()
{
cin >> st >> ed;
while (cin >> ss[tot] >> e[tot]) tot++;
solve();
return 0;
}