1. 程式人生 > >最長迴文序列(求出最長的並且打印出來)

最長迴文序列(求出最長的並且打印出來)

這兩道題都是求最長迴文的長度,在這裡我已開始想著就是沒有思路,想了半天,然後翻了會兒書,找到了一個知識點,那就是manacher演算法

這個演算法很好的解決了這道難題,然後我子啊百度上邊找啊找,找到了這個,最後也算是把這道題給理解了吧。

  1. 這道題主要是解決一個問題,那就是如果長度是計數或者偶數結果是不一樣的,但是這個演算法很巧妙地解決了這個難題,正如我給的那個連結說的一樣,
  2. 首先:大家都知道什麼叫回文串吧,這個演算法要解決的就是一個字串中最長的迴文子串有多長。這個演算法可以在On)的時間複雜度內既線性時間複雜度的情況下,求出以每個字元為中心的最長迴文有多長,
        這個演算法有一個很巧妙的地方,它把奇數的迴文串和偶數的迴文串統一起來考慮了。這一點一直是在做迴文串問題中時比較煩的地方。這個演算法還有一個很好的地方就是充分利用了字元匹配的特殊性,避免了大量不必要的重複匹配。


        演算法大致過程是這樣。先在每兩個相鄰字元中間插入一個分隔符,當然這個分隔符要在原串中沒有出現過。一般可以用‘#’分隔。這樣就非常巧妙的將奇數長度迴文串與偶數長度迴文串統一起來考慮了(見下面的一個例子,迴文串長度全為奇數了),然後用一個輔助陣列P記錄以每個字元為中心的最長迴文串的資訊。Pid]記錄的是以字元strid]為中心的最長迴文串,當以strid]為第一個字元,這個最長迴文串向右延伸了Pid]個字元。
        原串:    w aa bwsw f d
        新串:   # w# a # a # b# w # s # w # f # d #
    輔助陣列P:  1 2 1 2 3 2 1 2 1 2 1 4 1 2 1 2 1 2 1

        這裡有一個很好的性質,Pid-1就是該回文子串在原串中的長度(包括‘#’)。如果這裡不是特別清楚,可以自己拿出紙來畫一畫,自己體會體會。當然這裡可能每個人寫法不盡相同,不過我想大致思路應該是一樣的吧。
        好,我們繼續。現在的關鍵問題就在於怎麼在On)時間複雜度內求出P陣列了。只要把這個P陣列求出來,最長迴文子串就可以直接掃一遍得出來了。
        由於這個演算法是線性從前往後掃的。那麼當我們準備求Pi]的時候,i以前的Pj]我們是已經得到了的。我們用mx記在i之前的迴文串中,延伸至最右端的位置。同時用id這個變數記下取得這個最優mx時的id值。(注:為了防止字元比較的時候越界,我在這個加了‘
    #’的字串之前還加了另一個特殊字元‘$’,故我的新串下標是從1開始的)
    好,到這裡,我們可以先貼一份程式碼了。

    複製程式碼

    1. void pk()
      {
          int i;
          int mx = 0;
          int id;
          for(i=1; i<n; i++)
          {
              if( mx > i )
                  p[i] = MIN( p[2*id-i], mx-i );        
              else
                  p[i] = 1;
              for(; str[i+p[i]] == str[i-p[i]]; p[i]++)
                  ;
              if( p[i] + i > mx )
              {
                  mx = p[i] + i;
                  id = i;
              }
          }
      }

       程式碼是不是很短啊,而且相當好寫。很方便吧,還記得我上面說的這個演算法避免了很多不必要的重複匹配吧。這是什麼意思呢,其實這就是一句程式碼。 if( mx > i)     p[i]=MIN( p[2*id-i], mx-i); 就是當前面比較的最遠長度mx>i的時候,Pi]有一個最小值。這個演算法的核心思想就在這裡,為什麼P陣列滿足這樣一個性質呢?    (下面的部分為圖片形式)




        看完這個演算法,你有可能會覺得這種演算法在哪會用到呢?其實迴文串字尾陣列也可以做。只是複雜度是On log n)的,而且一般情況下也不會刻意去卡一個log n的演算法。可正好hdu就有這麼一題,你用字尾陣列寫怎麼都得T(當然應該是我寫得太爛了)。不信的話大家也可以去試試這題。
  3. 當然後邊就是我自己的演算法:
  4. #include <iostream>
    #include <vector>
    #include <list>
    #include <map>
    #include <set>
    #include <deque>
    #include <queue>
    #include <stack>
    #include <bitset>
    #include <algorithm>
    #include <functional>
    #include <numeric>
    #include <utility>
    #include <sstream>
    #include <iomanip>
    #include <cstdio>
    #include <cmath>
    #include <cstdlib>
    #include <cctype>
    #include <string>
    #include <cstring>
    #include <cmath>
    #include <ctime>
    #define MAX 100000000
    #define LOCA
    #define PI acos(-1.0)
    #include<stack>
    char b[30000];
    using namespace std;
    char str[1000001], s[2000002];
    int len, cas = 0, p[2000002];
    void build()//在這裡就是把一個個的s字串寫成一個接一連二的形式,方便不用考慮技術與歐舒,也就是這個才使得這個演算法特別的巧妙,也是我們應該要學習的地方,你,加油哦!@[email protected]
    {
        int i;
        s[0] = '@';
        s[1] = '#';
        len = strlen(str);
        for(i = 0; i < len; i++)
        {
            s[2 * i + 2] = str[i];
            s[2 * i + 3] = '#';
        }
        s[2 * len + 2] = '\0';
    }
    void solve()
    {
        int i, mx = 0, id, ans = 1;
        len = 2 * len  + 2;
        int xx=0;
        for(i = 1; i < len; i++)
        {
            if(mx > i) p[i] = min(p[2 * id - i], mx - i);
            else p[i] = 1;
            for(; s[i - p[i]] == s[i + p[i]]; p[i]++)
            if(p[i] + i > mx)
            {
                mx = p[i] + i;
                id = i;
            }
            if(ans<p[i]){ans=p[i];xx=i;};
        }
        printf("Case %d: %d\n", ++cas, ans - 1);
    }
    int main()
    {
        while(scanf("%s", str) != EOF)
        {
            if(strcmp(str, "END") == 0)
            break;
            build();
            solve();
        }
        return 0;
    }
    //dabcbab
    //babccbad
  5. 我在這裡最後解釋下怎麼把這個最長的迴文串給輸出出來:
  6. int t=0;
        stack<int> p;//說明一下,這個xx就是那個最長的迴文串的中心那個位置的下標,怎麼對那個xx賦值,只需要在ans=max(ans,p[i])改成if(ans<p[i]){ans=p[i];xx=i;}即可
        while(t<ans-1)//這個ans-1就是那個最長的迴文串的長度
        {
            if(s[xx]=='#')//其實我在這裡把他考慮複雜很多,其實根本沒必要使用棧,用一個簡單的陣列就行,或許你/會覺得我這個看起來很傻,不會用腦子,能偷懶怎麼就不偷一點呢,但是我們是在學習,學習這個只是為了懂得更多的知識點,因為我現在才大一,對於資料結構也就只是在演算法裡邊見過,掌握的也不是很深,所以想著還是多找幾個地方練練手,畢竟這個不是為了比賽,只是為了學到更多的東西,學的更熟練。這其實就是個過程,慢慢學吧!
            {
                p.push(s[t+1+xx]);//使用棧把id前邊的迴文串資料全都儲存起來,但是這裡儲存的時候就要分那個中心到底是以‘#’為中心還是以字母為中心呢?這樣豈不是不一樣的,需要分開考慮!
    //            cout<<s[t+1+xx];
            }
            else
                p.push(s[xx+t]);
    //            cout<<s[xx+t];
            t+=2;
        }
        int k=0;
        memset(b,0,sizeof b);
        while(!p.empty())//將佔裡邊的資料反向輸出來,這就是輸出為迴文串裡邊的左邊的字母
        {
            cout<<(b[k++]=p.top());
            p.pop();
        }
        if(s[xx]=='#')xx=0;//然後輸出中心右邊的資料,這裡我可以直接輸出的
        else xx=1;
        while(xx<=k-1)
        {cout<<b[k-1-xx];
        xx++;
        }
        cout<<endl;
    }

相關推薦

序列並且出來

這兩道題都是求最長迴文的長度,在這裡我已開始想著就是沒有思路,想了半天,然後翻了會兒書,找到了一個知識點,那就是manacher演算法 這個演算法很好的解決了這道難題,然後我子啊百度上邊找啊找,找

Python字串三重迴圈遍歷所有字串 新

前兩天自己寫了下,雖然對了,但是思路不太好,網上看了遍歷的思路,瞬間感覺之前好蠢,雖然本質上我的程式碼也是遍歷,但是思路不好。 正確思路就是先遍歷出所有長度大於等於2的字串,然後在逐一判斷這些字串是不是迴文字串就行了,思路清晰了,程式碼就簡單多了,10分鐘就寫完了。 d

【LeetCode】Longest Common Subsequence公共子序列某一解+LCS長度

Longest Common Subsequence 給出兩個字串,找到最長公共子序列(LCS),返回LCS的長度。 說明 最長公共子序列的定義: • 最長公共子序列問題是在一組序列(通常2個)中找到最長公共子序列(注意:不同於子串,LCS不需要是

leetcode簡單篇四百零九題

給定一個包含大寫字母和小寫字母的字串,找到通過這些字母構造成的最長的迴文串。 在構造過程中,請注意區分大小寫。比如 “Aa” 不能當做一個迴文字串。 注意: 假設字串的長度不會超過 1010。 示例 1: 輸入: “abccccdd” 輸出:

JAVA動態規劃--公共子序列問題LCS_subSequence的三種解法與公共子字串LCS_subString的兩種解法與LongestPalindrome

動態規劃法 經常會遇到複雜問題不能簡單地分解成幾個子問題,而會分解出一系列的子問題。簡單地採用把大問題分解成子問題,並綜合子問題的解匯出大問題的解的方法,問題求解耗時會按問題規模呈冪級數增加。 為了節約重複求相同子問題的時間,引入一個數組,不管它們是否對最終

LeetCode 409. C、C++、python

給定一個包含大寫字母和小寫字母的字串,找到通過這些字母構造成的最長的迴文串。 在構造過程中,請注意區分大小寫。比如 "Aa" 不能當做一個迴文字串。 注意: 假設字串的長度不會超過 1010。 示例 1: 輸入: "abccccdd" 輸出: 7 解釋:

字串Mancher演算法

給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為1000。示例 1:輸入: "babad" 輸出: "bab" 注意: "aba"也是一個有效答案。 示例 2:輸入: "cbbd" 輸出: "bb"思路:將字串中每一個元素作為中心算出其最大的迴文字串

manacher演算法

最長迴文Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 25520    Accepted Submission(s

manacher演算法串 和 hdu 3068

1. 迴文串定義 迴文串是一個正讀和反讀都一樣的字串,比如“aba”或者“abba”等等就是迴文串。 2. 最長迴文子串方法 最長迴文子串的長度方法可以有三種方法: 1) 樸素演算法是依次以每一個字元為中心向兩側進行擴充套件,時間複雜度是O(N^2)的; 2) 利用

網易程式設計題一,序列貪心法

迴文序列(貪心法) 如果一個數字序列逆置之後跟原序列是一樣的就稱這樣的數字序列為迴文序列。例如:{1, 2, 1}, {15, 78, 78, 15} , {112} 是迴文序列, {1, 2, 2}

特殊數字:5位數和6位數中各位上的數字之和為n的數。

問題描述   123321是一個非常特殊的數,它從左邊讀和從右邊讀是一樣的。   輸入一個正整數n, 程式設計求所有這樣的五位和六位十進位制數,滿足各位數字之和等於n 。 輸入格式   輸入一行,包含一個正整數n。 輸出格式   按從小到大的順序輸出滿足條件的整數,每個整

自動機2018ACM-ICPC南京賽區網路賽: I. Skr

I. Skr A number is skr, if and only if it's unchanged after being reversed. For example, "12321", "11" and "1" are skr numbers, but "12

判斷數、字串從左邊讀和從右邊讀一樣

1.判斷一個數是不是迴文數,如:"123321" #include <stdio.h> int main() {                  int m=1234;  //m是要判斷的數                  int n=0;  //n是反轉

hdu1231 大連續子序列DP之大子序列

和上一題一樣,只不過變為陣列。 #include <stdio.h> #include <string.h> #include <algorithm> using

【C語言】簡單的瞭解遞斐波那契,n的階乘,字串長度,把一個整型無符號,轉化為字元型並出來

簡單瞭解遞迴1.什麼是遞迴???程式設計程式呼叫自身的程式設計技巧稱為遞迴( recursion)遞迴做為一種演算法在程式設計語言中廣泛應用。 一個過程或函式在其定義或說明中有直接 或間接呼叫自身的一種方法,它通常把一個大型複雜的問題層層轉化為一個與原問題相似的 規模較小的問

【HDU - 3068】Manacher演算法,馬拉車演算法子串

題幹: 給出一個只由小寫英文字元a,b,c...y,z組成的字串S,求S中最長迴文串的長度.  迴文就是正反讀都是一樣的字串,如aba, abba等 Input 輸入有多組case,不超過120組,每組輸入為一行小寫英文字元a,b,c...y,z組成的字串S  兩

字串 JAVA版本【給編碼思想-適用於任何語言】

目的:在任意的字串中求出最長的迴文字串   思路:(適用於任何語言)   1、判斷當前給定的字串是否是相同的字串(也就是所有字元都相同),如果是直接返回了。   2、如果第一步沒有返回,就以非第一個字元為軸,分別求出以它為軸的,雙數迴文字串,

串-從動態規劃到"馬拉車"之路

預備知識: (1)在一個數軸上有兩點i和j(i<=j)關於點m對稱,那麼有 i = 2m-j;  證明: 因為 i<=j 且 i 和 j 關於 m 對稱,那麼有 (i + j)/ 2 = m  所以 i = 2m - j; (2)迴文串的對稱性:  由迴文串的

騰訊2016實習筆試題序列

問題描述 迴文串:首位相同的字串,如:abba   aca 迴文序列:在字串裡面刪除一個或多個字元,剩餘的字元組成迴文串。如:abbceda    刪除"ced",剩餘abba組成迴文序列 最長迴文

Manacher演算法------子串Java)

最長迴文子串 對於一個字串,請設計一個高效演算法,計算其中最長迴文子串的長度。 給定字串A以及它的長度n,請返回最長迴文子串的長度。 測試樣例: "abc1234321ab",12 返回:7 public class Main {      public st