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


     

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

     

  • 相关阅读:
    Sunday算法
    砝码称重 洛谷 1441
    树秀于林风必摧之——线段树
    常用stl(c++)
    Vue 根组件,局部,全局组件 | 组件间通信,案例组件化
    Win下JDK的安装和简单使用教程
    ubuntu服务器远程连接xshell,putty,xftp的简单使用教程
    ubuntu下安装pdo和pdo_mysql扩展
    服务器和域名的简单个人认知
    对大一一年的总结和对大二的规划
  • 原文地址:https://www.cnblogs.com/xiaoguapi/p/10400539.html
Copyright © 2020-2023  润新知