NOIP2002-字串变换

Description

已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则):

A_1A1​ ->B_1B1​

A_2A2​ -> B_2B2​

规则的含义为:在 AA中的子串 A_1A1​ 可以变换为B_1B1​,A_2A2​ 可以变换为 B_2B2​ …。

例如:AA='abcdabcd'BB='xyzxyz'

变换规则为:

‘abcabc’->‘xuxu’‘udud’->‘yy’‘yy’->‘yzyz’

则此时,AA可以经过一系列的变换变为BB,其变换的过程为:

‘abcdabcd’->‘xudxud’->‘xyxy’->‘xyzxyz’

共进行了33次变换,使得AA变换为BB。

Input

格式如下:

AA BB
A_1A1​ B_1B1​
A_2A2​ B_2B2​ |-> 变换规则

... ... /

所有字符串长度的上限为2020。

Output

输出至屏幕。格式如下:

若在1010步(包含1010步)以内能将AA变换为BB,则输出最少的变换步数;否则输出"NO ANSWER!"

Sample Input

abcd xyz
abc xu
ud y
y yz

Sample Output

3

Solution

题目大意是,给定起始状态A和终止转态B,以及一些变换规则,问多少步可以从A变换到B(或大于10步无解)。

显然可以用BFS搜索解决,但是注意一定要判重。这道题如果熟悉STL的话是很容易写出简洁的代码的,我用的string的replace构造转换后的字串,set判重。

这是我的第一份代码:

#include<cstdio>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
struct node{
string s;int step;
node(string s,int step):s(s),step(step){}//构造函数
};
string A,B,x,y;
vector<pair<string,string> > rule;
set<string> used;
queue<node> q;
bool bfs(){
q.push(node(A,));
used.insert(A);
while(!q.empty())
{
node h=q.front();q.pop();
if(h.step>) return false;
for(int i=;i<rule.size();++i)
{
int x=h.s.find(rule[i].first);//x是可以转换的起始位置
if(x!=-)
for(int j=x;j!=-;j=h.s.find(rule[i].first,j+))//寻找下一个可以转换的位置
{
string tmp=h.s;
tmp.replace(j,rule[i].first.length(),rule[i].second);//把tmp从j开始的 rule[i].first.length()个字符替换成rule[i].second
if(tmp==B){cout<<h.step+;return true;}
if(!used.count(tmp)){//一定要判重
q.push(node(tmp,h.step+));
used.insert(tmp);
}
}
}
}
return false;
}
int main()
{
cin>>A>>B;
while(cin>>x>>y) rule.push_back(make_pair(x,y));
if(!bfs()) cout<<"NO ANSWER!";
return ;
}

在洛谷上28ms就过了,但交到牛客网上T得飞起,第n次被牛客网的超强数据卡了,真想剁了牛客网。

但是本蒟蒻是不会放弃的,下面重点来了:

Optimization(优化)

注意到起始状态A和终止状态B都是确定的,那么我们可不可以从正反两个方向一起向中间搜索呢?答案是肯定的,这就是双向BFS。

效率

从题目来看,每次扩展有k(k<=66)个分支,最多扩展n(n<=10)层,那么BFS的计算量最坏情况近似地为k^n,这个数字还是相当吓人的(怪不得我会T)

但如果用双向BFS的话,时间效率的优化就不仅仅是一半那么简单,而是2*(k^(n/2)),效率大大提升。

实现

实现很简单,建两个队列,一个存正向的,另一个存反向的,很容易想到正反交替扩展,但这样其实是不科学的,会导致两边决策树发展情况失衡,降低时间效率。因此最好的方式应该是选择结点数较少的一边扩展,这样可以最大限度地维持两边决策树的平衡(这里只简单说一下,具体见网上的证明)。

还有一点要注意,题目中的规则是有单向性的,所以正向扩展时应从A_1->B_1,而反向扩展时应从B_1->A_1,这害我查错查了好久。。。

双向BFS优化代码:

#include<cstdio>
#include<queue>
#include<set>
#include<map>
#include<utility>
#include<vector>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
struct node{
string s;int step;
node(string s,int step):s(s),step(step){}
};
string A,B,x,y;
vector<pair<string,string> > rule;
set<string> used[];
map<string,int> dis[];//这里要多建一个dis来存到这个状态花的步数
queue<node> q[];//每一个都建两个,下标0存正向的,下标1存反向的
void expand(int k){
node h=q[k].front();q[k].pop();
for(int i=;i<rule.size();++i)
{
int x=(k&)?h.s.find(rule[i].second):h.s.find(rule[i].first);//正反是不同的
if(x!=-)
for(int j=x;j!=-;j=(k&)?h.s.find(rule[i].second,j+):h.s.find(rule[i].first,j+))
{
string tmp=h.s;
if(k&) tmp.replace(j,rule[i].second.length(),rule[i].first);
else tmp.replace(j,rule[i].first.length(),rule[i].second);
if(used[k^].count(tmp)){cout<<h.step++dis[k^][tmp];exit();}//如果另一个方向已经访问过tmp了,两边到tmp的步数之和即解
if(!used[k].count(tmp)){
q[k].push(node(tmp,h.step+));
dis[k][tmp]=h.step+;
used[k].insert(tmp);
}
}
}
return ;
}
bool bfs(){
q[].push(node(A,)),q[].push(node(B,));
dis[][A]=,dis[][B]=;
used[].insert(A),used[].insert(B);
while(!q[].empty()&&!q[].empty())
{
if(q[].front().step+q[].front().step>) return false;
q[].size()<q[].size()?expand():expand();//扩展结点数较少的一边
}
return false;
}
int main()
{
cin>>A>>B;
while(cin>>x>>y) rule.push_back(make_pair(x,y));
if(!bfs()) cout<<"NO ANSWER!";
return ;
}

尽管我懒到用STL的queue,还是成功AC牛客网(话说我的第一份代码是不是因为这个才挂的。。。)

2018-10-20

NOIP2002-字串变换【双向BFS】的更多相关文章

  1. NOIP2002 字串变换题解(双向搜索)

    65. [NOIP2002] 字串变换 时间限制:1 s   内存限制:128 MB [问题描述] 已知有两个字串A$, B$及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ ...

  2. [NOIP2002]字串变换 T2 双向BFS

    题目描述 已知有两个字串  A,B  及一组字串变换的规则(至多6个规则): A1−>B1 A2−>B2 规则的含义为:在  A$中的子串  A1可以变换为可以变换为B1.A2可以变换为可 ...

  3. 双向BFS—>NOIP2002 字串变换

    如果目标也已知的话,用双向BFS能很大提高速度 单向时,是 b^len的扩展. 双向的话,2*b^(len/2)  快了很多,特别是分支因子b较大时 至于实现上,网上有些做法是用两个队列,交替节点搜索 ...

  4. NOIP2002字串变换[BFS]

    题目描述 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为:在 A$中的子串 A1$ 可以变换为 B1$.A2 ...

  5. 「NOIP2002」「Codevs1099」 字串变换(BFS

    1099 字串变换 2002年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold   题目描述 Description 已知有两个字串 $A$, ...

  6. NOIP2002 字串变换

    题二 字串变换 (存盘名: NOIPG2) [问题描述]: 已知有两个字串 A$, B$ 及一组字串变换的规则(至多6个规则): A1$ -> B1$ A2$ -> B2$ 规则的含义为: ...

  7. P1032 字串变换 字符串BFS

    题目描述 已知有两个字串A,BA,B及一组字串变换的规则(至多66个规则): A_1A1​ ->B_1B1​ A_2A2​ -> B_2B2​ 规则的含义为:在 AA中的子串 A_1A1​ ...

  8. codevs1099字串变换(Bfs)

    /* 最少步数问题 妥妥的Bfs 很显然队列里存的是串(可能存个数也可以 就像8数码那样) 然后每次队首元素弄出来 能换的都换一遍 最后每次换完的新串入队前先判断到头了没 最后说一句 String大法 ...

  9. 洛谷 P1032 字串变换 (BFS)

    题目传送门 我即使是死了,钉在棺材里了,也要在墓里,用这腐朽的声带喊出 STL大法好 这题最麻烦的其实是处理字符串,真正的搜索部分我个人认为也就只有橙题或黄题的难度.而处理字符串,正如前面所说,STL ...

  10. 洛谷P1032 字串变换【bfs】

    题目链接:https://www.luogu.org/problemnew/show/P1032 题意: 给定一个原字符串和目标字符串,以及几个字符串变换的规则. 问能否根据这几个规则在十步之内把原字 ...

随机推荐

  1. Boruvka

    大概是这样的:一开始图中有\(n\)个连通块,每次操作我们选出各个连通块连出去的最短的边(如果有相同边权的边的话可以把序号作为第二关键字),然后把这些边加入最小生成树. 最坏的情况下每次操作都会让当前 ...

  2. git reset –mixed –soft –hard命令解释。

    直接看官方的解释. 其中HEAD代表版本库,index代表暂存区,另外还有一个我们增删改代码的工作区.所以官方解释翻译过来就是: --hard : 回退版本库,暂存区,工作区.(因此我们修改过的代码就 ...

  3. Spring Cloud 入门概括介绍

    出处: 拜托!面试请不要再问我Spring Cloud底层原理 概述 毫无疑问,Spring Cloud是目前微服务架构领域的翘楚,无数的书籍博客都在讲解这个技术.不过大多数讲解还停留在对Spring ...

  4. wordpress后台编辑如何显示定义的`style.css`样式

    wordpress后台编辑如何显示定义的style.css样式 由于公司官网采用wordpress进行搭建,但是却又自己设计页面,无奈主题只能自行构建了,直接修改wordpress自带的主题进行修改. ...

  5. sql认识

    DDL – Data Definition Language数据定义语言DML – Data Manipulation Language数据操作语言DCL – Data Control Languag ...

  6. vs2013在用户控件中添加jquery智能提示

    一.在script文件夹下面添加_references.js文件夹 二.把jquery文件拖到该文件中 保存,重新打开相应的文件,即出现智能提示

  7. centos 安装配置LAMP平台

    实验环境: [root@nmserver-7 html]# cat /etc/redhat-release CentOS release 7.3.1611 (AltArch) [root@nmserv ...

  8. Delphi 语句

  9. 如何正确训练一个 SVM + HOG 行人检测器

    这几个月一直在忙着做大论文,一个基于 SVM 的新的目标检测算法.为了做性能对比,我必须训练一个经典的 Dalal05 提出的行人检测器,我原以为这个任务很简单,但是我错了. 为了训练出一个性能达标的 ...

  10. linux把用户添加到组

    使用 usermod 命令 将现有的用户添加到多个次要组或附加组 # usermod -a -G GroupName UserName id 命令查看输出 # id UserName 用户添加到多个次 ...