如何找到字符串中的最长回文子串?

回文串就是正度和反读都是一样的字符串

例如:

第3位为中心: c a b a d a b a e

第5位为中心: c a b a d a b a e

使用中心扩展算法

我们已经知道了第3位为中心的a b a和第5位为中心的a b a d a b a是回文,那么判断第7位为中心的回文串的时候,由于回文的特性,就能够知道2-4和6-8对称

1. c a b a d a b a e  2-4位与6-8对称

2. c a b a d a b a e  2-4位是回文

3. c a b a d a b a e  推断6-8位也是回文

以第6位为中心,由于第4位的回文长度是1,所以它也只能是1,不需要再扩展区判断了

以第7位为中心的回文串计算,由之前分析已经知道最小长度是3了,而且从5位的回文中心来看,我们只知道第8位是什么,到是具体第九位是什么,根据之前的信息我们无法得知,因此需要进行中心扩展

整个算法的核心就是已知回文的右边界,只要在这个右边界的羽翼下,就有已知信息判断

算法步骤:

1.对字符串预处理,因为字符串可能为偶数,加入特殊符号#

2.然后遍历字符串,用一个数组来记录以该字符串为中心的回文长度,为了方便计算,在数组中存放记录长度的一半

3.每一次遍历字符串,如果该字符串在已知回文串最右边界的覆盖下,那么就计算其相对右边界回文串中心对称的位置,得出已知回文串的长度;

4.判断该长度和右边界,如果达到右边界,那么需要进行中心扩展.当然,如果第3步该字符没有在右边界的覆盖下,则直接进行中心扩展,进行中心扩展的同时更新右边界.

5.去掉特殊符号

代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Solution
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "cabadabae";
            s = LongestPalindrome(s);
            Console.WriteLine(s);
            Console.ReadKey();
        }

        static string LongestPalindrome(string s)
        {
            //1.0 预处理字符串
            string str = PreHandleString(s);
            //2.1 获取字符串的长度
            int len = str.Length;
            //2.2 右边界
            int rightSide = 0;
            //2.3 右边界对应的回文中心
            int rightCenter = 0;
            //2.4 回文的最长长度
            int longest = 0;
            //2.5 记录回文中心
            int center = 0;
            //2.6 用于记录以该字符为中心的长度的一半
            int[] halfLenArr = new int[len];
            //3.0 进行循环字符串
            for (int i = 0; i < len; i++)
            {
                //是否需要中心扩展
                bool needCal = true;
                //3.1 判断是不是在右边界之内
                if (rightSide > i)
                {
                    //根据回文特性计算
                    int leftCenter = 2 * rightCenter - i;
                    halfLenArr[i] = halfLenArr[leftCenter];
                    //如果超过了右边界,进行调整
                    if (halfLenArr[i] + i > rightSide)
                    {
                        halfLenArr[i] = rightSide - i;
                    }
                    //如果根据已知条件计算得出的最长回文小于右边界,则不需要扩展
                    if (halfLenArr[leftCenter] + i < rightSide)
                    {
                        needCal = false;
                    }
                }
                if (needCal)
                {
                    //3.2 由中心向两边扩展判断
                    while (i - 1 - halfLenArr[i] >= 0 && i + 1 + halfLenArr[i] < len)
                    {
                        if (str[i - 1 - halfLenArr[i]] == str[i + 1 + halfLenArr[i]])
                        {
                            halfLenArr[i]++;
                        }
                        else
                        {
                            //两边的值不同,跳出循环
                            break;
                        }
                    }
                    //3.3 更新回文中心以及右边界的值
                    rightCenter = i;
                    rightSide = i + halfLenArr[i];

                    //当前回文长度与最长回文长度做判断
                    if (halfLenArr[i] > longest)
                    {
                        //大于时,更新最长长度和回文中心
                        longest = halfLenArr[i];
                        center = i;
                    }
                }
            }
            //4.0 去掉特殊字符串(去掉#)
            StringBuilder sb = new StringBuilder();
            for (int i = center - longest + 1; i <= center + longest; i += 2)
            {
                sb.Append(str[i]);
            }
            return sb.ToString();
        }

        /// 
        /// 预处理字符串
        /// 
        /// 
        /// 
        private static string PreHandleString(string s)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("#");
            for (int i = 0; i < s.Length; i++)
            {
                sb.Append(s[i]);
                sb.Append("#");
            }
            return sb.ToString();
        }
    }
}

 

你可能感兴趣的:(算法)