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. Java中的模板设计模式,太实用了!

    顾名思义,模板设计模式就是将许多公用的常用的代码封装成一个模板,我们只需要实现不同的业务需求的代码,然后和模板组合在一起,那么就得到完整的逻辑. 在我们的日常开发中,常用的模板模式有两种实现方式:继承 ...

  2. 【6.24校内test】T2 不老梦

    [题目背景] 于万人中万幸得以相逢,刹那间澈净明通. 成为我所向披靡的勇气和惶恐,裂山海,堕苍穹. 爱若执炬迎风,炽烈而哀恸,诸般滋味皆在其中. 韶华宛转吟诵,苍凉的光荣,急景凋年深情难共. ——银临 ...

  3. HDU-4219-Randomization?

    题目描述 给定一棵\(n\)个节点的树,每条边的权值为\([0,L]\)之间的随机整数,求这棵树两点之间最长距离不超过\(S\)的概率. Input 第一行三个整数\(n,L,S\) 接下来n-1行, ...

  4. python爬虫常用第三方库

    这个列表包含与网页抓取和数据处理的Python库 网络 通用 urllib -网络库(stdlib). requests -网络库. grab – 网络库(基于pycurl). pycurl – 网络 ...

  5. 去除富文本中的html标签及vue、react、微信小程序中的过滤器

    在获取富文本后,又只要显示部分内容,需要去除富文本标签,然后再截取其中一部分内容:然后就是过滤器,在微信小程序中使用还是挺多次的,在vue及react中也遇到过 1.富文本去除html标签 去除htm ...

  6. linux:RAID(磁盘阵列)笔记

    RAID磁盘阵列简述:     RAID0(条带): 把多个同样大小的磁盘串联起来当做一个磁盘来用.         优点:读写速度快.         缺点:数据容易丢失(没有容错能力).     ...

  7. POJ 3249 Test for Job (拓扑排序+DP)

    POJ 3249 Test for Job (拓扑排序+DP) <题目链接> 题目大意: 给定一个有向图(图不一定连通),每个点都有点权(可能为负),让你求出从源点走向汇点的路径上的最大点 ...

  8. ssl多人多附件多格式邮件发送

    package com.dfmy.util; import java.io.File; import java.security.Security; import java.util.ArrayLis ...

  9. python gRPC简单示例

    Ubuntu18.04安装gRPC protobuf-compiler-grpc安装 sudo apt-get install protobuf-compiler-grpc protobuf-comp ...

  10. 机器学习聚类算法之K-means

    一.概念 K-means是一种典型的聚类算法,它是基于距离的,是一种无监督的机器学习算法. K-means需要提前设置聚类数量,我们称之为簇,还要为之设置初始质心. 缺点: 1.循环计算点到质心的距离 ...