<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    小明思考

    Just a software engineer
    posts - 124, comments - 36, trackbacks - 0, articles - 0
      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    問(wèn)題給定一個(gè)字符串s,分割s使得每個(gè)子串都是回文的(即倒過(guò)來(lái)和原字符串是一樣的,如aba)
    求最少的分割次數(shù)。

    比如對(duì)于“aaba”返回1.(分割方法:["a","aba"])

    public class Solution {
        public int minCut(String s) {
           //write your code here
        }
    }

    解法一:【遞歸】
    對(duì)于一個(gè)字符串,先找到第一段回文子字符串,然后再求余下的子串的最少分割數(shù),然后再加1,就可以得到一種分割結(jié)果,遍歷所以這樣的解法,就可以求出最小的分割次數(shù)。
    public class MinCut {
        private boolean isPalindrome(String s){
            int len = s.length();
            if(len>1){
                for(int i=0;i<len/2;++i){
                    if(s.charAt(i)!=s.charAt(len-i-1)){
                        return false;
                    }
                }
            }
            return true;
        }
        public int minCut(String s){
            if(isPalindrome(s)){
                return 0;
            }
            int min = 99999;

            int len =s.length();
            for(int i=1;i<len;++i){
                String ss = s.substring(0,i);
                if(isPalindrome(ss)){
                    int result = minCut(s.substring(i))+1;
                    if(result<min){
                        min = result;
                    }
                    if(min<=1){
                        break;
                    }
                }
            }
            return min;
        }
    public static void main(String[] args) throws IOException {
            long tick = System.currentTimeMillis();
            MinCut m = new MinCut();
            int result = m.minCut("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
            System.out.println("result:"+result+",costs:"+(System.currentTimeMillis()-tick)+" ms");
        }
    }

    這樣算法雖然可行,但是效率很低,對(duì)于小字符串還可以工作,對(duì)于上面的長(zhǎng)度為1400+的字符串就慢的驚人。

    因?yàn)橛写罅康闹貜?fù)計(jì)算。改進(jìn)一下,把已經(jīng)計(jì)算好的子串的最少分割次數(shù)的結(jié)果記錄下來(lái),應(yīng)該能快很多。

    Map<String,Integer> memos = new HashMap<String,Integer>();
        
        public int cut(String s){
            if(isPalindrome(s)){
                return 0;
            }
            if(memos.containsKey(s)){
                return memos.get(s).intValue();
            }
            int min = 99999;

            int len =s.length();
            for(int i=1;i<len;++i){
                String ss = s.substring(0,i);
                if(isPalindrome(ss)){
                    int result = cut(s.substring(i))+1;
                    if(result<min){
                        min = result;
                    }
                    if(min<=1){
                        break;
                    }
                }
            }
            memos.put(s, min);
            return min;
        }
        
        public int minCut(String s) {
            memos.clear();
            return cut(s);
        }

    果然,優(yōu)化后的結(jié)果要快很多,在我的機(jī)器上大概1200ms就可以求出結(jié)果了。

    解法二:【動(dòng)態(tài)規(guī)劃】
    其實(shí)這個(gè)問(wèn)題是一個(gè)典型的動(dòng)態(tài)規(guī)劃問(wèn)題。問(wèn)題的最優(yōu)解可以歸結(jié)為兩個(gè)子問(wèn)題的最優(yōu)解,再加上1就可以了,使用動(dòng)態(tài)規(guī)劃記錄所有的中間狀態(tài)就可以降低重復(fù)計(jì)算。
    狀態(tài)轉(zhuǎn)移公式如下:

    minCut(s,i,j) = 0 if(s[i..j] 是回文的)
    minCut(s,i,j) = min(minCut(s,i,k)+minCut(s,k+1,j))(i<k<j)

    為了減少回文的判斷,使用了兩個(gè)數(shù)組,M用來(lái)記錄最優(yōu)分割次數(shù),P用來(lái)保存子串的回文判斷結(jié)果。
    代碼如下:
    public int minCut(String s){
            int totalLength = s.length();
            char[] ch=s.toCharArray();
            int [][] M = new int[totalLength][totalLength];
            boolean [][] P = new boolean[totalLength][totalLength];
            for(int len=1;len<=totalLength;++len){
                for(int i=0;i<totalLength-len+1;++i){
                    int j = i+len-1;
                    if(len==1){
                        P[i][j]=true;
                    }
                    else if(len==2){
                        P[i][j]=(ch[i]==ch[j]);
                    }
                    else{
                        P[i][j]=(ch[i]==ch[j]) && P[i+1][j-1];
                    }
                    if(P[i][j]){
                        M[i][j] = 0;
                    }
                    else{
                        int min = 99999;
                        for(int k=i;k<j;++k){
                            int t = M[i][k]+M[k+1][j]+1;
                            if(min>t){
                                min = t;
                            }
                        }
                        M[i][j] = min;
                    }
                }
            }
            return M[0][totalLength-1];
        }

    這個(gè)算法的復(fù)雜度是O(n3)的復(fù)雜度,使用了三層循環(huán)。對(duì)于上面的字符串大概需要花4000ms左右的時(shí)間。

    有沒(méi)有辦法把復(fù)雜度降為O(n2)呢?對(duì)于長(zhǎng)度為L(zhǎng)的字符串的最少切割次數(shù)等于L-1的切割次數(shù)+1或者如果最后一個(gè)字符能夠跟前面的子字符串構(gòu)成回文的話,就等于去除該子串的剩余部分的切割次數(shù)+1。
    有些拗口,還是看狀態(tài)轉(zhuǎn)移公式吧:

    minCut(j)= min(
    minCut(j-i-1)+1: if s(i,j) is palindrom
    where 0<=i<j ,
    minCut(j-1)+1)

    同時(shí)優(yōu)化一下回文的判斷,減少函數(shù)調(diào)用。

    private boolean isPalindrome(char[] ch, int i,int j){
            while(i<j){
                if(ch[i]!=ch[j]){
                    return false;
                }
                ++i;
                --j;
            }
            return true;
        }
    public int minCut(String s){
            int totalLength = s.length();
            int[] M = new int[totalLength];
            char[] ch = s.toCharArray();
            M[0]=0;
            for(int i=1;i<totalLength;++i){
                int min = 99999;
                for(int j=0;j<=i;++j){
                    int cut = 99999;
                    if(isPalindrome(ch,j,i)){
                        if(j>0){
                            cut = M[j-1]+1;
                        }
                        else{
                            cut = 0;
                        }
                        if(min>cut){
                            min = cut;
                        }
                    }
                }
                
                M[i]=min;
            }
            return M[totalLength-1];
        }

    優(yōu)化后的結(jié)果,大概只需要200ms左右了。

    評(píng)論

    # re: 回文字符串的切割問(wèn)題[未登錄](méi)  回復(fù)  更多評(píng)論   

    2013-04-17 12:39 by Harry
    object Palindrome {
    def isPelindrome(l:String):Boolean = {
    val fhalf = l.take(l.length/2)
    val shalf = l.takeRight(l.length/2).reverse
    if (fhalf==shalf) true else false
    }
    def minCut(s:String,partition:Int):Int = {
    val ss = s.take(partition)
    val ts = s.takeRight(s.length - partition)
    if (isPelindrome(ss)&&isPelindrome(ts)) 1 else 0
    }
    def main(args: Array[String]): Unit = {
    val stime = System.currentTimeMillis()
    val t = {for (i <- 1 to 14000) yield "a"}.foldLeft(""){case (c,s)=> s+c}
    val r = for (i <- 1 to t.size) yield minCut(t,i)
    println(r.sum)
    println(System.currentTimeMillis() - stime)
    }

    }

    result: 14,000 "a", takes 2669 ms

    # re: 回文字符串的切割問(wèn)題  回復(fù)  更多評(píng)論   

    2013-10-13 22:07 by selldogs
    第三個(gè)的復(fù)雜度也是 O(N^3) , 你每次判斷是否是回文 不是也有一個(gè)O(N)的循環(huán)么

    # re: 回文字符串的切割問(wèn)題  回復(fù)  更多評(píng)論   

    2014-05-22 14:12 by 2dog
    @selldogs
    同意
    這算法本身就是O(n^3)的
    主站蜘蛛池模板: 麻豆视频免费观看| 日韩精品电影一区亚洲| 亚洲成av人无码亚洲成av人| 亚洲综合久久夜AV | 亚洲大片免费观看| 国产亚洲男人的天堂在线观看| 亚洲色自偷自拍另类小说| 日韩欧毛片免费视频| xxxxx做受大片视频免费| 亚洲视频一区二区在线观看| 国产精品免费播放| 最好看的中文字幕2019免费| 添bbb免费观看高清视频| 亚洲激情校园春色| 久久久久亚洲精品天堂久久久久久| 91九色视频无限观看免费| 日韩在线观看视频免费| 亚洲伦理中文字幕| 亚洲精品字幕在线观看| 国产精品无码素人福利免费| 91成人免费观看| 精品多毛少妇人妻AV免费久久| 亚洲一区二区三区精品视频| 中文亚洲AV片在线观看不卡 | 国产精品偷伦视频免费观看了| 亚洲国产精品成人久久久| 亚洲精品乱码久久久久66| 国产福利免费在线观看| 在线免费观看亚洲| 国产线视频精品免费观看视频| 亚洲视频免费在线播放| 日韩少妇内射免费播放| 亚洲乱色伦图片区小说| 亚洲大香伊人蕉在人依线| 亚洲av无码无在线观看红杏| 亚洲日本韩国在线| 亚洲AV无码乱码在线观看性色扶 | xx视频在线永久免费观看| 十八禁在线观看视频播放免费| 边摸边吃奶边做爽免费视频99 | 永久免费的网站在线观看|