CH2908【字串变换】(双向BFS,KMP)

 

洛谷有个题目弱化版 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;
}
View Code

刚才我们枚举当前字符串串的位置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+bfs

实际上两个算法差不多,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;
}   
View code

 

猜你喜欢

转载自www.cnblogs.com/xiaoguapi/p/10400539.html