亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

Manacher's Algorithm ---Java實現

2024-09-10 09:23:25
4
0

這個馬拉車算法Manacher‘s Algorithm是用來查找一個字符串的最長回文子串的線性方法,由一個叫Manacher的人在1975年發明的,這個方法的最大貢獻是在于將時間復雜度提升到了線性,這是非常了不起的。對于回文串想必大家都不陌生,就是正讀反讀都一樣的字符串,比如 "bob", "level", "noon" 等等,那么如何在一個字符串中找出最長回文子串呢,可以以每一個字符為中心,向兩邊尋找回文子串,在遍歷完整個數組后,就可以找到最長的回文子串。但是這個方法的時間復雜度為O(n*n),并不是很高效,下面我們來看時間復雜度為O(n)的馬拉車算法。

由于回文串的長度可奇可偶,比如"bob"是奇數形式的回文,"noon"就是偶數形式的回文,馬拉車算法的第一步是預處理,做法是在每一個字符的左右都加上一個特殊字符,比如加上'#',那么

bob    -->    #b#o#b#

noon    -->    #n#o#o#n# 

這樣做的好處是不論原字符串是奇數還是偶數個,處理之后得到的字符串的個數都是奇數個,這樣就不用分情況討論了,而可以一起搞定。接下來我們還需要和處理后的字符串t等長的數組p,其中p[i]表示以t[i]字符為中心的回文子串的半徑,若p[i] = 1,則該回文子串就是t[i]本身,那么我們來看一個簡單的例子:

# 1 # 2 # 2 # 1 # 2 # 2 #
1 2 1 2 5 2 1 6 1 2 3 2 1

由于第一個和最后一個字符都是#號,且也需要搜索回文,為了防止越界,我們還需要在首尾再加上非#號字符,實際操作時我們還需給開頭加上個非#號字符'$',結尾加一個'@'。通過p數組我們就可以找到其最大值和其位置,就能確定最長回文子串了,那么下面我們就來看如何求p數組,需要新增兩個輔助變量mx和id,其中id為最大回文子串中心的位置,mx是回文串能延伸到的最右端的位置(很多文章都這樣說,其實并不是,根據下面的代碼就能看出來,mx應該是已找到的回文子串所能到達的最右端,而id是最右端回文子串對應的中心),這個算法的最核心的一行如下:

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

可以這么說,這行要是理解了,那么馬拉車算法基本上就沒啥問題了,那么這一行代碼拆開來看就是

如果mx > i, 則 p[i] = min(p[2 * id - i], mx - i)

否則, p[i] = 1

當 mx - i > P[j] 的時候,以S[j]為中心的回文子串包含在以S[id]為中心的回文子串中,由于 i 和 j 對稱,以S[i]為中心的回文子串必然包含在以S[id]為中心的回文子串中,所以必有 P[i] = P[j]。

對于 mx <= i 的情況,無法對 P[i]做更多的假設,只能P[i] = 1,然后再去匹配了。

參見如下實現代碼:

    public static void main(String[] args) {
        String str = "abcde";
        Scanner s = new Scanner(System.in);
        while (true) {
            System.out.print("請輸入字符串:");
            String line = s.nextLine();
            if (line.equals("exit")) break;
            else {
                System.out.println("最大回文子串: "+Manacher(line));
            }
        }
 
    }
    public static String Manacher(String s) {
        // Insert '#'
        String t = "$#";
        for (int i = 0; i < s.length(); ++i) {
            t += s.charAt(i);
            t += "#";
        }
        t += "@";
        // Process t
        int[] p = new int[t.length()];;
        int mx = 0, id = 0, resLen = 0, resCenter = 0;
        for (int i = 1; i < t.length()-1; ++i) {
            p[i] = mx > i ? Math.min(p[2 * id - i], mx - i) : 1;
            while (((i - p[i])>=0) && ((i + p[i])<t.length()-1) && (t.charAt(i + p[i]) == t.charAt(i - p[i])))
                ++p[i];
            if (mx < i + p[i]) {
                mx = i + p[i];
                id = i;
            }
            if (resLen < p[i]) {
                resLen = p[i];
                resCenter = i;
            }
        }
        return s.substring((resCenter - resLen) / 2, (resCenter - resLen) / 2 + resLen-1);
    }

0條評論
作者已關閉評論
汪****翔
3文章數
0粉絲數
汪****翔
3 文章 | 0 粉絲
汪****翔
3文章數
0粉絲數
汪****翔
3 文章 | 0 粉絲
原創

Manacher's Algorithm ---Java實現

2024-09-10 09:23:25
4
0

這個馬拉車算法Manacher‘s Algorithm是用來查找一個字符串的最長回文子串的線性方法,由一個叫Manacher的人在1975年發明的,這個方法的最大貢獻是在于將時間復雜度提升到了線性,這是非常了不起的。對于回文串想必大家都不陌生,就是正讀反讀都一樣的字符串,比如 "bob", "level", "noon" 等等,那么如何在一個字符串中找出最長回文子串呢,可以以每一個字符為中心,向兩邊尋找回文子串,在遍歷完整個數組后,就可以找到最長的回文子串。但是這個方法的時間復雜度為O(n*n),并不是很高效,下面我們來看時間復雜度為O(n)的馬拉車算法。

由于回文串的長度可奇可偶,比如"bob"是奇數形式的回文,"noon"就是偶數形式的回文,馬拉車算法的第一步是預處理,做法是在每一個字符的左右都加上一個特殊字符,比如加上'#',那么

bob    -->    #b#o#b#

noon    -->    #n#o#o#n# 

這樣做的好處是不論原字符串是奇數還是偶數個,處理之后得到的字符串的個數都是奇數個,這樣就不用分情況討論了,而可以一起搞定。接下來我們還需要和處理后的字符串t等長的數組p,其中p[i]表示以t[i]字符為中心的回文子串的半徑,若p[i] = 1,則該回文子串就是t[i]本身,那么我們來看一個簡單的例子:

# 1 # 2 # 2 # 1 # 2 # 2 #
1 2 1 2 5 2 1 6 1 2 3 2 1

由于第一個和最后一個字符都是#號,且也需要搜索回文,為了防止越界,我們還需要在首尾再加上非#號字符,實際操作時我們還需給開頭加上個非#號字符'$',結尾加一個'@'。通過p數組我們就可以找到其最大值和其位置,就能確定最長回文子串了,那么下面我們就來看如何求p數組,需要新增兩個輔助變量mx和id,其中id為最大回文子串中心的位置,mx是回文串能延伸到的最右端的位置(很多文章都這樣說,其實并不是,根據下面的代碼就能看出來,mx應該是已找到的回文子串所能到達的最右端,而id是最右端回文子串對應的中心),這個算法的最核心的一行如下:

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

可以這么說,這行要是理解了,那么馬拉車算法基本上就沒啥問題了,那么這一行代碼拆開來看就是

如果mx > i, 則 p[i] = min(p[2 * id - i], mx - i)

否則, p[i] = 1

當 mx - i > P[j] 的時候,以S[j]為中心的回文子串包含在以S[id]為中心的回文子串中,由于 i 和 j 對稱,以S[i]為中心的回文子串必然包含在以S[id]為中心的回文子串中,所以必有 P[i] = P[j]。

對于 mx <= i 的情況,無法對 P[i]做更多的假設,只能P[i] = 1,然后再去匹配了。

參見如下實現代碼:

    public static void main(String[] args) {
        String str = "abcde";
        Scanner s = new Scanner(System.in);
        while (true) {
            System.out.print("請輸入字符串:");
            String line = s.nextLine();
            if (line.equals("exit")) break;
            else {
                System.out.println("最大回文子串: "+Manacher(line));
            }
        }
 
    }
    public static String Manacher(String s) {
        // Insert '#'
        String t = "$#";
        for (int i = 0; i < s.length(); ++i) {
            t += s.charAt(i);
            t += "#";
        }
        t += "@";
        // Process t
        int[] p = new int[t.length()];;
        int mx = 0, id = 0, resLen = 0, resCenter = 0;
        for (int i = 1; i < t.length()-1; ++i) {
            p[i] = mx > i ? Math.min(p[2 * id - i], mx - i) : 1;
            while (((i - p[i])>=0) && ((i + p[i])<t.length()-1) && (t.charAt(i + p[i]) == t.charAt(i - p[i])))
                ++p[i];
            if (mx < i + p[i]) {
                mx = i + p[i];
                id = i;
            }
            if (resLen < p[i]) {
                resLen = p[i];
                resCenter = i;
            }
        }
        return s.substring((resCenter - resLen) / 2, (resCenter - resLen) / 2 + resLen-1);
    }

文章來自個人專欄
文章 | 訂閱
0條評論
作者已關閉評論
作者已關閉評論
0
0