乍一見這個題目,我的心是畏懼的。然後又生出必須要戰勝的決心。至於爲什麼感情如此豐富呢?主要還是因爲這道題曾經成爲我的一道坎坷。大一上學期學習C語言的時候,剛開始特別迷戀編程,天天刷題到圖書館關門。那時候真是一道題做一天正常的很(誇張誇張,不過半天確實有)。不過大一呀,正是玩樂的好時機,於是乎心慢慢鬆了。這道題卡了第55題,對於那時剛剛接觸編程才2個月的我當真麻煩的很,加上C語言指針什麼的,更是頭腦發熱。於是在半天思索還不見方案的前提下,我選擇了跳過這道題,繼續刷其他題。看起來情有可原,實際上卻是畏懼!哎哎哎。。。不說了。總之今天看到這道題,第一感覺就是征服,不僅要解決,還要用各種方法解決!
不說了,晾題:
給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 長度最長爲1000。
示例:
輸入: "babad" 輸出: "bab" 注意: "aba"也是有效答案
先說看到這道題目的思路吧,我的反應如下:
1、將字符串s進行遍歷。
2、判斷當前的節點是否符合迴文串條件,奇偶兩種情況。
3、符合迴文串的像兩端擴展,並且添加到容器中。
4、獲的list容器,存儲着所有的迴文串,和第一個字符(如果沒有2個或2個以上字符組成的迴文串,就以第一個字符爲最長迴文串計算)。
5、對list進行排序,選取最長的迴文串作爲結果返回。
一下就是我首次的代碼:
public static String longestPalindrome(String s) {
//存儲不同長度的迴文串
List<StringBuffer> sbList = new ArrayList<StringBuffer>();
boolean isTwoCycle = false;
boolean isThreeCycle = false;
//默認將第一個字符傳入list,作爲默認最長迴文串
sbList.add(new StringBuffer(String.valueOf(s.charAt(0))));
for(int i = 1; i < s.length() ;i++){
//判斷當前字符是否構成迴文串,是奇數還是偶數
if(s.charAt(i) == s.charAt(i-1)){
isTwoCycle = true;
}
if(i >= 2 && (s.charAt(i)==s.charAt(i-2))){
isThreeCycle = true;
}
int cursor = 1;
//如果是以兩個重複字爲迴文串
if(isTwoCycle){
//字符串下標不能小於0,同時不能超過字符串長度
while(i-cursor>=0 && i+cursor-1<s.length()){
//符合迴文串規則
if(s.charAt(i-cursor) == s.charAt(i+cursor-1)){
cursor++;
}else{
break;
}
}
sbList.add(new StringBuffer(s.substring(i-cursor+1,i+cursor-1)));
//可能該字符同時符合奇偶迴文串,如ccc,故分開置爲1
cursor = 1;
isTwoCycle = false;
}
//如果是以三個字,爲開始的迴文串,同時不能超過字符串長度
if(isThreeCycle){
while(i-cursor-1>=0 && i+cursor-1<s.length()){
if(s.charAt(i-cursor-1) == s.charAt(i+cursor-1)){
cursor++;
}else{
break;
}
}
sbList.add(new StringBuffer(s.substring(i-cursor,i+cursor-1)));
cursor=1;
isThreeCycle = false;
}
}
Collections.sort(sbList, new Comparator<StringBuffer>() {
@Override
public int compare(StringBuffer sb1, StringBuffer sb2) {
return -(sb1.length() - sb2.length());
}
});
return sbList.get(0).toString();
}
興沖沖地寫完,然後提交居然一次性成功,心裏竊喜還沒有停下,就突然蹦出一個134ms。納尼?134ms,仔細一看最快的,我去!7ms,神啊!有木有?心裏的竊喜馬上止住,趕忙打開7ms的代碼,那個簡潔,那個巧妙,那個精美……
鄙人花了一些時間仔細看了一下代碼,然後自己書寫了一遍,如下:
/**
* 可以看出,整個算法的核心主要有三點:
* 1、將str字符串轉換成數組進行處理
* 2、將偶迴文處理,使之變成奇迴文的處理方式(重)
* 3、使用int[]數組,合理地記錄字符數組的下標,並以此來比較迴文串長度大小
* Created by v_shampoowang on 2018/4/11.
*/
public class PalindromeString {
public static void main(String[] args){
System.out.println(new PalindromeString().
longestPalindrome("aba"));
}
//求最長迴文子串
public String longestPalindrome(String str){
if(str.length()<=1) return str;
char[] characters = str.toCharArray();
//用於存放str中最長迴文子串所對應的下標
int[] range = {0,1};
for(int i = 0;i<characters.length;i++){
i = helper(i,characters,range);
}
return str.substring(range[0],range[1]);
}
private int helper(int index,char[] c,int[] range) {
int low = index;
int high = index;
//如果遇到相同字符,則high進位,如abba ,這樣偶迴文子串也可以當做奇迴文處理了
while(high<c.length-1 && c[high]==c[high+1]){
high++;
}
int cursor = high;
while(high+1<c.length&&low-1>=0&&c[low-1]==c[high+1]){
low--;
high++;
}
if(high-low+1>range[1]-range[0]){
range[0] = low;
range[1] = high + 1;
}
return cursor;
}
}
看完別人的代碼,此處不由得開始深思,我用了134ms!別人只用了7ms,近20倍的差距!仔細深析一下原因:
最大的原因肯定是自己考慮的點不對,未能能將多種處理方法歸併到一起,是積累不夠,也是敏感度地欠缺。
第二,變量使用率上不夠,很多變量的龐大,但是使用次數不多。
第三,產生了很多不必要的東西,比如list存儲了很多沒有必要的數據。
第四,儘量使用原生的數組,能夠將消耗變得最小,同時也能最大地提升效率。以前學C的時候就是使用數組,現在工作了一段時間,對容器的依賴加重了好多。
總結:雖然自己現在的算法還不如很多大一學生,並且很多情況下對於我這個業務工程師來說,算法的精妙度體現不出來。但是算法的編寫又何嘗不是提升思維能力,提升學習興趣的過程。或許我現在只是一個業務工程師,但是說不定,幾年之後,我就像算法轉型了呢?就算不是,加深對數據結構和算法的理解,也是身爲一個程序員鎖必不可少的過程!
在此互勉,共同進步。歡迎大家批評指正。