算法-字符串之最長無重復(fù)子串

算法字符串系列的第四篇文章,計算給定字符串的最長無重復(fù)子串幔妨。


這篇文章主要介紹兩種方法鹦赎,一種是基于hash的思想,一種是基于dp(動態(tài)規(guī)劃)+hash來實現(xiàn)误堡,這兩種方法都不難古话,容易理解。下面會詳細介紹锁施。

1.hash實現(xiàn)

這類問題應(yīng)該學(xué)會分析結(jié)果的特點陪踩,比如說前面的最長回文子串,是利用了回文串的對稱性悉抵,那么無重復(fù)子串呢肩狂,想到無重復(fù),可以想到可以使用hash基跑,當(dāng)新的字符來了婚温,有沖突描焰,就說明前面是有重復(fù)的媳否。
算法思想:兩次循環(huán)得到所有的子串,用hash判斷是否重復(fù)荆秦。

代碼
代碼中需要注意的地方:
1.以字符對應(yīng)的ASCII碼作為hash值篱竭,visit[str[i]]=0,說明str[i]這個字符還沒有出現(xiàn)過步绸,=1說明有重復(fù)掺逼。
2.在每次內(nèi)層循環(huán)重新開始的時候,都要將visit初始化為0瓤介,每次內(nèi)層循環(huán)求的是str[i...j]之間的最長子串吕喘,要判斷他們之間是否有重復(fù)赘那,所以要確保i...j這個范圍內(nèi)的visit都沒初始化為0了,否則出現(xiàn)i...j之間的字符和這個范圍之外的字符重復(fù)就會導(dǎo)致結(jié)果出錯氯质。

/*求所有的子串募舟,用hash判斷是否是重復(fù)的,*/
void LNRS_hash(char *str, int size)
{
    int i, j;
     for (i = 0; i < size; ++i)
    {
        memset(visit, 0, sizeof(visit));//初始化visit為0
        visit[str[i]] = 1;
        for (j = i + 1; j < size; ++j)
        {
            if (visit[str[j]] == 0)
            {
                visit[str[j]] = 1;
            }
            else
            {
                if (j - i > longest)
                {
                    longest = j - i;
                    start = i;
                }
                break;
             }
        }
        if ((j == size) && (j - i > longest))
        {
            longest = j - i;
            start = i;
        }
    }
}

結(jié)果:

基于hash求最長無重復(fù)子串

2.動態(tài)規(guī)劃+hash

首先理解闻察,動態(tài)規(guī)劃算法的思想拱礁,將問題分解為子問題的解,找到重疊子問題和最優(yōu)子結(jié)構(gòu)辕漂,對需要重復(fù)計算的結(jié)果進行存儲呢灶。在這里最優(yōu)子結(jié)構(gòu)很好理解,在所有無重復(fù)子串中钉嘹,長度最長的就是最優(yōu)的鸯乃。而重疊子問題呢?簡單的理解隧期,就是當(dāng)前的LNRS串可以由上一次計算的LNRS得到飒责。

重疊子問題:我們考慮兩種情況。

  1. 當(dāng)前的字符和前面的字符沒有重復(fù)仆潮,那么到當(dāng)前字符的最長無重復(fù)子串就應(yīng)該在上次求得的LNRS串長度+1宏蛉,;
  2. 如果當(dāng)前的字符有沖突性置,那么有兩種情況需要分析拾并。第一,如果和它沖突的字符在當(dāng)前最長無重復(fù)子串的起始位置前面鹏浅,那么很明顯嗅义,對計算當(dāng)前的LNRS串沒有影響,還是在上次基礎(chǔ)上+1隐砸;如果沖突字符在當(dāng)前的LNRS串開始位置之后之碗,那么就從后一個位置計算無重復(fù)子串的長度。

而hash在這里就是幫助我們判斷是否重復(fù)季希,而不用回溯子串褪那。

代碼
理解了上面提到的重疊子問題之后,下面的問題就簡單多了式塌。
代碼中需要注意的地方:

  1. dp[]記錄第i位的無重復(fù)子串博敬,這個子串不一定是全局最長子串,但是針對第i個字符是最長的峰尝。所以需要比較所有的dp[i]偏窝,來確定最長的。
  2. visit[i]在這里不止是當(dāng)做hash來使用了,它不止用來判斷是否重復(fù)祭往,還記錄最近的重復(fù)位置伦意。eg:abcdahijka,針對最后一個a硼补,visit[a]記錄的應(yīng)該是第二個a的位置默赂,記錄第一個a的位置毫無意義,因為從第一個a后面的b...到最后一個a之間括勺,還有一個a,所以其實b到第二個a之間的字符肯定不會在a3的最長重復(fù)子串內(nèi)缆八。
/* LNRS dp + hash 記錄下標(biāo) */
void LNRS_dp_hash(char * str, int size)
{
    int *dp = (int*)malloc(sizeof(int)*size);

    memset(visit, -1, sizeof(visit));
    memset(dp, 0, sizeof(dp));
    dp[0] = 1;
    visit[str[0]] = 0;
    int last_start = 0;

    for (int i = 1; i < size; i++) {
        if (visit[str[i]] == -1){
            dp[i] = dp[i - 1] + 1;
            visit[str[i]] = i;//記錄字符的下標(biāo)
        }
        else {/*和visit[str[i]]位置的字符沖突*/
            if (last_start <= visit[str[i]])
            {
                dp[i] = i - visit[str[i]];
                last_start = visit[str[i]] + 1;
                visit[str[i]] = i;//更新最近的重復(fù)位置
          }
            else{
                dp[i] = dp[i - 1] + 1;
                visit[str[i]] = i;//記錄字符的下標(biāo)
            }
        }

        if (dp[i]>longest)
        {
            longest = dp[i];
            start = i - longest + 1;
        }
    }
}

結(jié)果:

dp+hash最長無重復(fù)子串

3.dp+hash優(yōu)化的問題

我們看dp[i] = dp[i-1]+1;可以理解為就是在更新當(dāng)前i位的最優(yōu)解,在看代碼中更新dp[i]的部分疾捍,再使用到dp[i-1]的地方都只是在上一次的基礎(chǔ)上+1奈辰,看到這里我們就很容易做出優(yōu)化策略了÷叶梗可以不用n位長的數(shù)組dp[i]來存儲當(dāng)前的最優(yōu)解奖恰,而只用一個變量來記錄就可以了,優(yōu)化方案很簡單宛裕,就不貼代碼了瑟啃。
優(yōu)化:用一個變量來代替dp[]。

總結(jié):

最長回文子串的問題:關(guān)注子串中的對稱關(guān)系揩尸。
最長無重復(fù)子串為題:關(guān)注子串中字符的唯一性蛹屿,善用hash。
動態(tài)規(guī)劃法岩榆,對需要重復(fù)計算的問題错负,可以選保存下來,減少計算的時間復(fù)雜度勇边。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犹撒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子粒褒,更是在濱河造成了極大的恐慌识颊,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奕坟,死亡現(xiàn)場離奇詭異祥款,居然都是意外死亡,警方通過查閱死者的電腦和手機执赡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門镰踏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來函筋,“玉大人沙合,你說我怎么就攤上這事〉剩” “怎么了首懈?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵绊率,是天一觀的道長。 經(jīng)常有香客問我究履,道長滤否,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任最仑,我火速辦了婚禮藐俺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泥彤。我一直安慰自己欲芹,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布吟吝。 她就那樣靜靜地躺著菱父,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剑逃。 梳的紋絲不亂的頭發(fā)上浙宜,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機與錄音蛹磺,去河邊找鬼粟瞬。 笑死,一個胖子當(dāng)著我的面吹牛萤捆,可吹牛的內(nèi)容都是我干的亩钟。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼鳖轰,長吁一口氣:“原來是場噩夢啊……” “哼清酥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蕴侣,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤焰轻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后昆雀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辱志,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年狞膘,在試婚紗的時候發(fā)現(xiàn)自己被綠了揩懒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡挽封,死狀恐怖已球,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤智亮,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布忆某,位于F島的核電站,受9級特大地震影響阔蛉,放射性物質(zhì)發(fā)生泄漏弃舒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一状原、第九天 我趴在偏房一處隱蔽的房頂上張望聋呢。 院中可真熱鬧,春花似錦颠区、人聲如沸坝冕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喂窟。三九已至,卻和暖如春央串,著一層夾襖步出監(jiān)牢的瞬間磨澡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工质和, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稳摄,地道東北人。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓饲宿,卻偏偏與公主長得像腋妙,于是被迫代替她去往敵國和親良瞧。 傳聞我的和親對象是個殘疾皇子指煎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問題, 分享了一些自己做題目的經(jīng)驗靡菇。 張土汪:刷leetcod...
    土汪閱讀 12,748評論 0 33
  • 1. 問題定義 最長不重復(fù)子串:一個字符串中最長的沒有重復(fù)字符的子串。舉個?? : abcabcbb 最長子串 a...
    林大鵬閱讀 11,893評論 0 2
  • 題目 對于一個字符串,請設(shè)計一個高效算法国夜,找到字符串的最長無重復(fù)字符的子串長度减噪。給定一個字符串A及它的長度n,請返...
    IT_Matters閱讀 2,112評論 0 1
  • 上一篇KMP算法之后好幾天都沒有更新车吹,今天介紹最長回文子串筹裕。 首先介紹一下什么叫回文串,就是正著讀和倒著讀的字符順...
    zero_sr閱讀 2,306評論 2 8
  • 動態(tài)規(guī)劃(Dynamic Programming) 本文包括: 動態(tài)規(guī)劃定義 狀態(tài)轉(zhuǎn)移方程 動態(tài)規(guī)劃算法步驟 最長...
    廖少少閱讀 3,296評論 0 18