欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java花式解決'分割回文串 ii'問(wèn)題詳解

 更新時(shí)間:2021年12月22日 09:02:20   作者:富春山居_ZYY  
最學(xué)習(xí)動(dòng)態(tài)規(guī)劃思想的路上,遇見(jiàn)了‘分割回文串問(wèn)題’,如臨大敵啊,題目聽(tīng)起來(lái)蠻簡(jiǎn)單,思考起來(lái)卻也沒(méi)那么容易,本文將為大家詳細(xì)介紹幾種解決分割回文串 ii問(wèn)題的辦法,需要的可以參考一下

前言

最學(xué)習(xí)動(dòng)態(tài)規(guī)劃思想的路上,遇見(jiàn)了‘分割回文串問(wèn)題',如臨大敵啊,題目聽(tīng)起來(lái)蠻簡(jiǎn)單,思考起來(lái)卻也沒(méi)那么容易,比解決問(wèn)題更頭疼的是如何將解決方法進(jìn)行優(yōu)化,使得時(shí)間空間復(fù)雜度盡量的小,經(jīng)過(guò)了反復(fù)的掙扎思考,終于總結(jié)出來(lái)了這一篇 分割回文串 ii 的文章,花式解決該問(wèn)題,總有一款適合你。

牛客鏈接

題目

給出一個(gè)字符串s,分割s使得分割出的每一個(gè)子串都是回文串

計(jì)算將字符串s分割成回文分割結(jié)果的最小切割數(shù)

例如: 給定字符串s=“aab”,

返回1,因?yàn)榛匚姆指罱Y(jié)果[“aa”,“b”]是切割一次生成的。

思路分析

首先,已知字符串s的長(zhǎng)度為len,想要求前l(fā)en個(gè)字符串被切割成回文串所需要的最小切割數(shù),就會(huì)很自然的想到求前i個(gè)字符形成的字符串切割成回文串需要的最小切割數(shù),即為狀態(tài)F(i)。

然后,會(huì)想到前i個(gè)字符形成的字符串分割變成回文串需要的最大切割數(shù)為i-1。例如字符串"aab",切2刀形成長(zhǎng)度為1的回文串"a",“a”,“b”。

再然后,關(guān)鍵在于求解最小切割數(shù)的過(guò)程,這里采取暴力求解,定義變量j,使之小于i,在我們已知狀態(tài)F(j)的情況下(即前j個(gè)字符形成的字符串的最小切割次數(shù)),如果[j+1,i]是回文串,那么再來(lái)上一刀就可以求出當(dāng)前用最少的切割次數(shù)。那么此時(shí)F(i)= min(F(i),F(xiàn)(j)+1),意思就是在上一次求得的前i個(gè)字符串的分割次數(shù)和這一次求得的次數(shù)進(jìn)行對(duì)比,取最小值。(注:這里的[j+1,i]指的是字符串第j+1個(gè)字符到第i個(gè)字符的意思,并非字符串下標(biāo)索引,寫(xiě)代碼時(shí),轉(zhuǎn)換成索引就應(yīng)該是求下標(biāo)為j-1的字符到下標(biāo)為i的字符形成的字符串是否為回文串)

緊接著,就該實(shí)現(xiàn)判斷是否為回文串的方法,簡(jiǎn)單的思想就是,為該方法提供字符串s,提供子串的起始下標(biāo)start與終點(diǎn)下標(biāo)end,start<end的條件下,使start向后走,end向前走,但凡對(duì)應(yīng)到字符串s中的字符不一樣,就說(shuō)明不是回文串,返回false,如果成功遍歷完了循環(huán),說(shuō)明是回文串,返回true。

最后,將F(len)的值返回。

動(dòng)歸四角度:

1.狀態(tài)定義F(i):字符串s的前i個(gè)字符的最小分割次數(shù);

2.狀態(tài)間的轉(zhuǎn)移方程定義:F(i)=min(F(i),F(xiàn)(j)+1),0 <= j < i,且F(j)為已知狀態(tài),當(dāng)[j+1,i]為回文串時(shí),執(zhí)行此狀態(tài)轉(zhuǎn)移方程

3.狀態(tài)的初始化:F(i) = i-1,注意F(0)為-1;

例如,當(dāng)字符串為"aa",因?yàn)閇1,2]為回文串,F(xiàn)(2)= min(F(2),F(xiàn)(0)+1)=min(1,0)= 0,得到正確答案

4.返回結(jié)果 :F(s.length());

案例說(shuō)明

為了方便理解,這里采取了更長(zhǎng)的字符串"aabaa",一步步帶你走過(guò)程。

初級(jí)代碼

import java.util.*;
public class Solution {
    //判斷是否為回文串
    public boolean func(String s,int start,int end) {
        while(start<end) {
            if(s.charAt(start)!=s.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
    public int minCut (String s) {
        int len = s.length();//字符串的長(zhǎng)度
        if(len == 0) return 0;//當(dāng)長(zhǎng)度為0時(shí)直接返回0
        int[] count = new int[len + 1];//用于記錄狀態(tài)
        //狀態(tài)初始化
        for(int i = 0;i <= len;i ++) {
            count[i] = i-1;
        }
        for(int i = 1;i <= len;i++) {
            for(int j = 0;j <= i-1;j++) {
                if(func(s,j,i-1)) {
                    count[i] = Math.min(count[i],count[j]+1);//狀態(tài)轉(zhuǎn)移方程
                }
            }
        }
        return count[len];//返回結(jié)果
    }
}

在整個(gè)進(jìn)行狀態(tài)計(jì)算的過(guò)程中,兩層for循環(huán)時(shí)間復(fù)雜度為O(N2),判斷是否為回文串的方法時(shí)間復(fù)雜度為O(N),因此總的來(lái)說(shuō),總的時(shí)間復(fù)雜度為O(N3)

代碼升級(jí)

可以看出來(lái),用上面的代碼時(shí)間復(fù)雜度還是比較高的,因此代碼還需升級(jí)才是

1.回文串動(dòng)歸

首先,關(guān)于回文串的判斷方法,每次判斷是否要進(jìn)行狀態(tài)轉(zhuǎn)移方程時(shí)都要調(diào)用回文串方法,這真的有必要嗎,或許也可以使用動(dòng)態(tài)規(guī)劃的思想將每種字符子串是否為回文串的狀態(tài)記錄下來(lái)。

狀態(tài)四角度:

1.狀態(tài)定義F(i,j):字符區(qū)間[i,j]是否為回文串

2.狀態(tài)間的轉(zhuǎn)移方程定義F(i,j):

如果i == j,表示單字符,F(xiàn)(i,j) = true;

如果j == i+1,表明倆字符是緊挨著的,如果在總字符串s中對(duì)應(yīng)的字符相同,F(xiàn)(i,j)= true,反之F(i,j) = false;

其他的情況中,F(xiàn)(i,j) = (s.charAt(i) == s.charAt(j)) && F(i+1,j-1);

該轉(zhuǎn)移方程的意思為字符首尾字符相同,且去掉字符區(qū)間的首位字符后的字符區(qū)間的狀態(tài)F(i+1,j-1)仍然為回文串才證明[i,j]字符串區(qū)間為回文串即F(i,j)= true

3.狀態(tài)的初始化:F(i,j) = false

4.返回結(jié)果狀態(tài)二維布爾類(lèi)型數(shù)組

注:由于在狀態(tài)轉(zhuǎn)移的過(guò)程中,求F(i,j)會(huì)只用到之前已經(jīng)計(jì)算過(guò)的狀態(tài)F(i+1,j-1),這就意味著i需要從后向前遍歷,使用的是已經(jīng)更新過(guò)結(jié)果的值

import java.util.*;
public class Solution {
    //判斷是否為回文串
    public boolean[][] func2 (String s) {
        int len = s.length();//字符串的長(zhǎng)度
        boolean[][] ret = new boolean[len][len];
        //記錄狀態(tài)的二維數(shù)組,默認(rèn)值為false
        //由于i<=j<len,所以ret數(shù)組實(shí)際只更新了一半
        for(int i = len;i >= 0;i--) {
            for(int j = i;j<len;j++) {
                if(i == j) {
                    ret[i][j] = true; //單字符比為回文串
                }else if(j == i+1) {
                    if(s.charAt(i) == s.charAt(j)) {
                        ret[i][j] = true; //相鄰字符相同為回文串
                    }else{
                        ret[i][j] = false;//相鄰字符不同就不是回文串
                    }
                }else{
                    ret[i][j] = (s.charAt(i) == s.charAt(j)) && ret[i+1][j-1];
                    //其余轉(zhuǎn)移情況
                }
            }
        }
        return ret;//返回結(jié)果
    }
    public int minCut (String s) {
        int len = s.length();
        if(len == 0) return 0;
        int[] count = new int[len + 1];
        //狀態(tài)初始化
        for(int i = 0;i <= len;i ++) {
            count[i] = i-1;
        }
        boolean[][] ret = func2(s);//調(diào)用判斷回文串方法,獲得所有字符子串的是否為回文串的情況
        for(int i = 1;i <= len;i++) {
            for(int j = 0;j <= i-1;j++) {
                //直接在ret數(shù)組中找結(jié)果,避免反復(fù)調(diào)用回文串判斷方法
                if(ret[j][i-1]) {
                    count[i] = Math.min(count[i],count[j]+1);//狀態(tài)轉(zhuǎn)移方程
                }
            }
        }
        return count[len];//返回結(jié)果
    }
}

在該方法中,判斷回文串的方法時(shí)間復(fù)雜度為O(N2),但因?yàn)樵谥鞣椒ㄖ兄徽{(diào)用了一次,且回文串判斷方法中只更新了一般的值,因此總的時(shí)間復(fù)雜度為O(N2)~O(2*N2)

2.綜合動(dòng)歸

可以看的出來(lái)上面的代碼還是比較長(zhǎng)的,回文串判斷方法用到了兩層循環(huán),主方法也用到了兩層循環(huán),這不也是優(yōu)化的方向蠻,或許可以把它們放在同一個(gè)兩層循環(huán)中。

注:由于回文串判斷方法中的i是一定要從后向前遍歷的,因此主函數(shù)的初識(shí)值就需要調(diào)整為count[i] = len - i - 1,返回的結(jié)果為F(0)

import java.util.*;
public class Solution {
    public int minCut(String s) {
        int len = s.length();//字符串的長(zhǎng)度
        if(len == 0) return 0;
        int []count = new int[len+1]; //存放最小分割次數(shù)狀態(tài)的數(shù)組
        boolean [][]p = new boolean[len][len];//存放[i,j]字符區(qū)間是否為回文串的二維數(shù)組
        for(int i = 0; i <= len; i++) count[i] = len - i - 1;//狀態(tài)初始化
        for(int i = len-1;i >= 0;i--){
            for(int j = i;j < len;j++){
                //j-i<2 條件成立且第一個(gè)條件成立包含著單個(gè)字符串和相鄰字符串的情況
                //p[i+1][j-1] 為 ture 且第一個(gè)條件成立則代表著其他的回文串狀態(tài)轉(zhuǎn)移類(lèi)型
                //以上情況有一項(xiàng)成立則F(i,j)為 ture
                if(s.charAt(i) == s.charAt(j) && (j-i<2||p[i+1][j-1])){
                    p[i][j] = true;
                    count[i] = Math.min(count[i],count[j+1]+1);//狀態(tài)轉(zhuǎn)移方程
                }
            }
        }
        return count[0];//返回結(jié)果
    }
}

通過(guò)這樣的方法,直接將時(shí)間復(fù)雜度降到了O(N2)

3.奇思妙想

上面幾種方法,需要將回文串的判斷狀態(tài)都記錄下來(lái),且判斷回文串的方法都是從子字符串的兩頭向中間進(jìn)行判斷,或許有一種方法,可以直接不用記錄下來(lái)每種子字符串的是否為回文串的狀態(tài),并且從中間向兩頭進(jìn)行判斷回文串。

可以設(shè)置兩個(gè)變量i和j,[i,j]且j==i代表著下標(biāo)為i的單個(gè)字符,必定是回文串,F(xiàn)(j+1)= min(F(j+1),F(xiàn)(i)+1),以此為中心,i--,j++,如果區(qū)間兩頭的字符相同,說(shuō)明[i-1,j+1]的區(qū)間字符串為回文串,在不超出原字符串s的總區(qū)間[0,len-1]的循環(huán)情況下,重復(fù)上面的操作,直到循環(huán)條件不成立

回文串可能是奇數(shù)個(gè)字符,也可能是偶數(shù)個(gè)字符,上面的情況是奇數(shù)個(gè)字符的情況,換成偶數(shù)個(gè)字符的情況只需要判斷[i,i+1]是否為回文串,如果是,就參考上面的方式,以此為中心向兩頭展開(kāi),求以[i,i+1]為中心最長(zhǎng)的回文串,從而求出每個(gè)狀態(tài)的最小分割數(shù)。

案例說(shuō)明:

import java.util.*;
public class Solution {
    public int minCut(String s) {
        int len = s.length();//字符串的長(zhǎng)度
        if(len == 0) return 0;
        int[] count = new int[len + 1];
        for(int i = 0; i <= len; i++) count[i] = i - 1;//狀態(tài)初始化
        for(int i = 0; i < len; i++) {
            func3(s, i, i, count);//奇數(shù)個(gè)字符的回文串
            func3(s, i, i + 1, count);//偶數(shù)個(gè)字符的回文串
        }
        return count[len];//返回結(jié)果
    }     
    private  void func3(String s, int i, int j, int[] count) {
        //不超過(guò)字符串s的區(qū)間范圍且下標(biāo)i的字符和下標(biāo)j的字符相等的條件下向兩頭擴(kuò)展,得到最長(zhǎng)的回文串,以此來(lái)求出狀態(tài)
        while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) {
            count[j + 1] = Math.min(count[j + 1], count[i] + 1); //狀態(tài)轉(zhuǎn)移
            --i;//左區(qū)間擴(kuò)展一格
            ++j;//右區(qū)間擴(kuò)展一格
        }
        return;
    }  
} 

到此這篇關(guān)于Java花式解決'分割回文串 ii'問(wèn)題詳解的文章就介紹到這了,更多相關(guān)Java解決分割回文串 ii內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Intellij IDEA調(diào)試技巧的深入講解

    Intellij IDEA調(diào)試技巧的深入講解

    這篇文章主要給大家介紹了關(guān)于Intellij IDEA調(diào)試技巧的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Maven插件構(gòu)建Docker鏡像的實(shí)現(xiàn)步驟

    Maven插件構(gòu)建Docker鏡像的實(shí)現(xiàn)步驟

    這篇文章主要介紹了Maven插件構(gòu)建Docker鏡像的實(shí)現(xiàn)步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • SpringBoot之自定義Banner詳解

    SpringBoot之自定義Banner詳解

    這篇文章主要介紹了SpringBoot之自定義Banner詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • spring boot設(shè)置過(guò)濾器、監(jiān)聽(tīng)器及攔截器的方法

    spring boot設(shè)置過(guò)濾器、監(jiān)聽(tīng)器及攔截器的方法

    這篇文章主要給大家介紹了關(guān)于spring boot設(shè)置過(guò)濾器、監(jiān)聽(tīng)器及攔截器的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Kotlin實(shí)現(xiàn)靜態(tài)方法

    Kotlin實(shí)現(xiàn)靜態(tài)方法

    這篇文章主要介紹了Kotlin實(shí)現(xiàn)靜態(tài)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • Java?Object類(lèi)equals方法

    Java?Object類(lèi)equals方法

    這篇文章主要介紹了Java?Object類(lèi)equals方法,Object類(lèi)位于java.lang包中,java.lang包包含著Java最基礎(chǔ)和核心的類(lèi),在編譯時(shí)會(huì)自動(dòng)導(dǎo)入,下文更多相關(guān)介紹,需要的小伙伴可以參考一下
    2022-04-04
  • 詳解Spring獲取配置的三種方式

    詳解Spring獲取配置的三種方式

    這篇文章主要為大家詳細(xì)介紹了Spring獲取配置的三種方式:@Value方式動(dòng)態(tài)獲取單個(gè)配置、@ConfigurationProperties+前綴方式批量獲取配置以及Environment動(dòng)態(tài)獲取單個(gè)配置,感興趣的可以了解一下
    2022-03-03
  • 一文詳解gRPC快速整合SpringCloud

    一文詳解gRPC快速整合SpringCloud

    這篇文章主要為大家介紹gRPC快速整合SpringCloud的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • Elasticsearch開(kāi)發(fā)AtomicArray使用示例探究

    Elasticsearch開(kāi)發(fā)AtomicArray使用示例探究

    這篇文章主要為大家介紹了Elasticsearch AtomicArray使用示例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • Java紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu)與算法解析

    Java紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu)與算法解析

    紅黑樹(shù)問(wèn)題是各大計(jì)算機(jī)考研命題以及面試算法題目中的熱門(mén),接下來(lái)我們?yōu)榇蠹覉D解紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu)與算法解析,需要的朋友可以參考下
    2021-08-08

最新評(píng)論