题目
前言
今天上洛谷发现,我终于从蓝名升为绿名了。。开森。。。
回到正题,由于这学期在学java,就想着用java敲。结果被自己秀的头皮发麻,用java最好要了解它源码的底层实现原理,不然很可能会用错的,这种错误就很难找了。在调了两个小时我的java代码后还是不行,我就去恰饭了。晚上我用C/C++把这题又敲了一遍,交上去结果跟之前的java代码一样,在比对了题解区的代码和我的代码,后来才发现在处理字符串的一处细节出错了,不过大体思路还是正确的。心态炸裂。
在调试这个题目时我发现了,这个题目的测试数据不够完善!
下面,我将会以解释如何用java写这一题。
分析
这个题目是在洛谷普及训练场的广搜专题,用广搜无疑了。这一点我就不赘述了。除此之外,这题的难点就是在:扩展这颗广搜树时,要进行子串搜索和替换!这个在题解区的实现方法比较多,我来介绍一下我的实现方法。
1、java中的String类的replace()方法和StringBuilder类的略有不同
//从下标fromIndex开始到主串末尾的范围内,搜索子串str。搜到,返回第一次出现的下标;否则返回-1 int indexOf(String str, int fromIndex); //我们的需求是:仅仅替换第一次出现的子串。但String类并没有该方法 //String类 String replace(charSequence target, charSequence replacement); //详情查看编译器的提示 //StringBuilder类 //将主串的[start, end)部分替换为str,返回一个新的StringBuilder StringBuilder replace(int start, int end, String str);
有了上面第1和第3个方法,就可以非常方便地实现题目所需要的子串搜索的需要(具体见代码)。当然C++的string也有类似的两个方法!
2、StringBuilder类并没有重写equals()方法,换言之HashSet<StringBuilder>是没有意义的
这题去重可以用set的,当然我看到题解区有用map的,没太大的必要,但问题不大也不是重点。
本来我用StringBuilder类的,后来在上网查了一下,并且自己查看源码之后,发现StringBuilder似乎并没有重写equals()方法。了解过set底层源码(这我就不多说了,感兴趣的可以自行百度)的应该知道,装在set里面的类需要重写hashCode()和equals()。故用set装自定义类时要注意重写这两个方法。
3、java中的队列是哪一个类?
队列完全可以用数组去模拟。但要封装一个队列类,用链表还是最有优势的。java集合中的LinkedList类是用链表实现的一种动态数组,里面的方法完全满足队列的需要,可以当队列使用。
4、测试数据不够完善
举个简单的例子:
xababaz(中间的a属于两个"aba") xabyz
aba y容易看出来,有两种替换方式,得到的结果分别是:xybaz和xabyz。假如有这种特殊数据,我觉得题解区有一部分代码过不了。
5、读入优化:使用Scanner类读取数据略慢,可以模仿大神使用IO流自定义输入类
这个我也不多说,学过IO流的同学不妨可以敲一下
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
using namespace std;
typedef struct Node
{
string str;
int steps;
Node() {}
Node(string s, int a):str(s),steps(a) {}
}Node;
string l[25]; //可能出现abc->123 abc->456的情况,不能使用map<string,string>
string r[25];
int n;
string src; //源串
string dest; //目标串
set<string> st; //装已经搜过的字符串
/*
xababaz xabyz
aba y
*/
void bfs()
{
queue<Node> q;
q.push(Node(src, 0));
int ans=0;
while(!q.empty())
{
Node head=q.front();
q.pop();
if(st.count(head.str)) //已经搜过
continue;
if(head.steps>10) //ans=0
break;
if(head.str==dest) //若src==dest,ans=0
{
ans=head.steps;
break;
}
st.insert(head.str);
for(int i=0;i<n;i++) //枚举所有规则
{
int index=0;
while((index=head.str.find(l[i], index))!=string::npos)//对于某一条规则,可能不止一个地方可以替换
{
string tmp=head.str; //创一个副本
string s=tmp.replace(index, l[i].length(), r[i]);
//index+=l[i].length(); //如果用的这一行代码,可以AC。但对于上面的特例输出:NO ANSWER!
index++; //用这一行代码,对于上面的特例输出:1。但是上面那行代码快几十ms
q.push(Node(s, head.steps+1));
}
}
}
if(ans==0)
cout<<"NO ANSWER!"<<endl;
else
cout<<ans<<endl;
}
int main()
{
cin>>src>>dest;
while(cin>>l[n]>>r[n])
n++;
bfs();
return 0;
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.StringTokenizer;
public class Main
{
static String src; //源串A
static String dest; //目标串B
static String[][] rule=new String[25][2];
static int n=0; //共有多少条规则
static class Node
{
String str;
int steps;
public Node() {}
public Node(String str, int steps)
{
this.str = str;
this.steps = steps;
}
}
static class MyScanner
{
BufferedReader reader; //字符缓冲流
StringTokenizer tokenizer; //用于分隔字符串
public MyScanner(InputStream is)
{
reader=new BufferedReader(new InputStreamReader(is), 32768); //字节流(System.in)->字符流->字符缓冲流
tokenizer=null;
}
public String next()
{
while(tokenizer==null || !tokenizer.hasMoreTokens())
{
try {
tokenizer=new StringTokenizer(reader.readLine()); //用字符串缓冲流读取一行,然后分隔
} catch (IOException e) {
e.printStackTrace();
}
}
return tokenizer.nextToken();
}
public boolean ready() throws IOException
{
return reader.ready();
}
}
public static void bfs()
{
HashSet<String> used=new HashSet<>(); //装搜过的字符串。要求重写过hashCode()和equals()方法
LinkedList<Node> queue=new LinkedList<>();
queue.add(new Node(src, 0));
int ans=0;
while(!queue.isEmpty())
{
Node head=queue.removeFirst();
if(used.contains(head.str))
continue;
if(head.steps>10) //ans=0
break;
if(head.str.equals(dest)) //若A==B,也属于无解。正好也符合ans=0
{
ans=head.steps;
break;
}
used.add(head.str);
for(int k=0;k<n;k++) //枚举规则
{
int index=0;
while((index=head.str.indexOf(rule[k][0], index))!=-1)
{
StringBuilder s=new StringBuilder(head.str).replace(index, index+rule[k][0].length(), rule[k][1]);
queue.add(new Node(s.toString(), head.steps+1));
index+=rule[k][0].length();
}
}
}
if(ans==0)
System.out.println("NO ANSWER!");
else
System.out.println(ans);
}
public static void main(String[] args) throws IOException
{
MyScanner msc=new MyScanner(System.in);
src=msc.next();
dest=msc.next();
while(msc.ready())
{
rule[n][0]=msc.next();
rule[n][1]=msc.next();
n++;
}
/*
Scanner sc=new Scanner(System.in);
src=sc.next();
dest=sc.next();
while(sc.hasNext())
{
rule[n][0]=sc.next();
rule[n][1]=sc.next();
n++;
}
*/
bfs();
}
}