題目
前言
今天上洛谷發現,我終於從藍名升爲綠名了。。開森。。。
回到正題,由於這學期在學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();
}
}