• 【字符串/广搜】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;
    }
  • 相关阅读:
    C# 获取 域(AD) 用户信息
    如何在 MOSS 2007 启用 Session
    安装 卸载 回收 sharepoint 解决方案 moss wss wsp
    [转]挑战Windows极限:物理内存
    霍特卡的 CSLA.Net 3.6 Windows及Silverlight的正式版 昨天发布 同时CSLA.Net 业务对象 2008 英文版图书可以网上订购
    转帖如何在不联网的情况下安装 Silverlight Tools
    jQuery 选择器的使用
    关闭 Windows Server 2008 用户权限控制(UAC)
    jQuery 的第一个例子
    jQuery for Asp.Net 一步一步从入门到精通(附 jQuery API 彩色大图)
  • 原文地址:https://www.cnblogs.com/jiangyuechen/p/13087126.html
Copyright © 2020-2023  润新知