【字符串/广搜】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;
}

 

posted @ 2020-07-06 10:20  梦中霜雪梨花白  阅读(211)  评论(0编辑  收藏  举报