5.连续最长回文字符串

brute force

就是暴力搜索。

三层循环,左右边界以及判断是否为回文字符串

def brute_force(s):
    pal_str =""
    for i in range(len(s)):
        for j in range(i, len(s)):
            while i <= j:
                if s[i] != s[j]:
                    continue
                i += 1
                j -= 1
        if len(pal_str) < j-i+1;
        	pal_str=s[i,j+1]
  	return pal_str

Dynamic Programming

考虑"ababa"的例子,如果知道'"bab"'是回文串,那么边上两个字母相同,就有整个字符串为回文字串。

定义\(P(i,j)\)

\[P(i,j)=\left\{ \begin{aligned} \text{True} &\quad S_{i,j}\text{是回文串}\\ \text{False} &\quad \text{其他} \end{aligned} \right. \]

显然有

\[P(i,j)=(P(i+1,j−1) \quad \text{and} \quad S_i==S_j) \]

而且有

\[\begin{aligned} P(i,i) &= true \\ P(i, i+1) &= (S_i == S_{i+1} ) \end{aligned} \]

  def DP(s):
    table = [[False for i in range(len(s))] for j in range(len(s))]
		sub_str = ""
		lens = 1
		while lens < len(s):
			for start in range(len(s)):
				end = start + lens -1
				if end >= len(s):
					break
				print(start, end)
				table[start][end] = (lens == 1 or lens == 2 or table[start+1][end-1]) \
						 and s[start] == s[end]
				if table[start][end] and len(sub_str) < end-start +1:
					sub_str = s[start:end+1]
			lens += 1
		return sub_str

Expand Around Center

对于任一字符,依此为中心向两边扩展。直到不是回文串为止。记录长度最大的。

def expandAroundCenter(s):
	start = 0 
	end = 0
	for i in range(len(s)):
		len1 = expand(s, i, i)
		len2 = expand(s, i, i+1)
		max_len = max(len1,len2)
		
		if max_len >= end -start + 1:
			start = i - int((max_len - 1) // 2)
			end   = i + int(max_len // 2)
	return s[start:end + 1]

def expand(s, left, right):
	L = left
	R = right
	while L >=0 and R < len(s) and s[L] == s[R]:
		L -= 1
		R += 1
	return R - L - 1

Manacher's Algorithm 马拉车算法

利用已知的对称信息和回文串的对称性得出其他位置的对称信息,并且不用考虑字符串长度是奇数还是偶数的,因为首先我们对字符串进行处理:在字符串的首尾和字符间加上"#",如"nbbn"改变后为"#n#b#b#n#",再在首位加上"&",所以最终经过处理的字符串为"&#n#b#b#n#"。另外我们还需要辅助一个和处理后长度一致的int数组,该数组用来存字符串对应位置的最长回文长度加上本身(就是最长回文数+1),如下:

Happy

如字符串第二个b两边对称的长度为1,所以它对应的int数组内的值为2。有了这个表格,我们就知道,只要求出字符串对应位置上int数组的值,那么回文串自然就出来了,那int[]数组怎么求出来呢?也就是怎么利用已知的对称信息和回文串的对称性得出其他位置的对称信息呢?首先假设最长回文所在位置为id,到最长回文长度的位置为idMax,如果我们要求i位置上的最长回文长度(也就是int[i]的值),可以根据int[j]的值,前提条件是(0<=j<i,所以int[j]是已知的),那么是什么原理呢?分为两种情况:

  • i <= idMax,通过id的位置找到和i位置对称的j点

Happy

  • 如过int[j] < idMax - i,那么int[i]就等于int[j]

    对称的半径在范围以内

  • 如果int[j] >= idMax - i,那么int[i]的值大于或者等于int[j]。然后再在这个位置继续向左右延伸判断是否还有回文长度。

    此时显然不满足对称关系了,因为越界了。

  • i > idMax时,所以中心点为i的回文还没有匹配,只有通过循环去匹配了。

public static String longestPalindrome(String s) {
    if (s.length() <= 1){
        return s;    
    } 
    StringBuilder stringBuilder = new StringBuilder("$");
    for (char c : s.toCharArray()) {
        stringBuilder.append("#");
        stringBuilder.append(c);
    }
    stringBuilder.append("#");
    String str = stringBuilder.toString();

    int id = 0;
    int idMax = 0;
    int index = 0;
    int maxLength = 0;

    int p[] = new int[str.length()];

    for (int curr = 1; curr < str.length(); curr++) {
        int j = 2 * id - curr;

        // p[curr] = idMax > curr ? Math.min(p[symmetryId], idMax - curr) : 1;
        // 更容易理解的写法
        if (idMax > curr)
        {
            if (p[j] < idMax - curr)
                p[curr] = p[j];
            else
                p[curr] = idMax - curr;
        } 
        else
        {
            p[curr] = 1;
        }

        while (curr + p[curr] < str.length() && str.charAt(curr + p[curr]) \
               == str.charAt(curr - p[curr])) {
            p[curr]++;
        }

        if (curr + p[curr] > idMax) {
            id = curr;
            idMax = curr + p[curr];
        }

        if (p[curr] > maxLength) {
            maxLength = p[curr];
            index = curr;
        }
    }
    return s.substring((index - maxLength) / 2, (index + maxLength) / 2 - 1);
}
posted @ 2021-07-14 23:11  青铜时代的猪  阅读(52)  评论(0)    收藏  举报