520,我会处理回文数了,你会了么?(dp+中心扩散)

2023-11-04

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。

示例 2:

输入: “cbbd”
输出: “bb”

方法一:暴力匹配(Brute Force)

●根据回文子串的定义,枚举所有长度大于等于2的子串,依次判断它们是否是文;
●在具体实现时,可以只针对大于“当前得到的最长回文子串长度"的子串进行"回文验证”;
●在记录最长回文子串的时候,可以只记录“当前子串的起始位置”和“子串长度”,不必做截取。这一步我们放在后面的方法中实现。
说明:力解法时间复杂度高,但是思路清晰、编写简单。由于编写正确性的可能性很大,可以使用暴力匹配算法检验我们编写的其它算法是否正确。

java

	public static String longestPalindrome(String s) {
		int len = s.length();
		if (len < 2) {
			return s;
		}

		int maxLen = 1;
		int begin = 0;
		// 每次都会检查下标是否越界,因此先转化成 字符数组
		char[] charArray = s.toCharArray();
		// 枚举所有长度大于一的子串
		for (int i = 0; i < len - 1; i++) {
			for (int j = i + 1; j < len; j++) {
				if (j - i + 1 > maxLen && validPalindromic(charArray, i, j)) {
					maxLen = j - i + 1;
					begin = i;
				}
			}
		}
		return s.substring(begin, begin + maxLen);
	}

	/**
	 * 验证子串是否是回文子串
	 * 
	 * @param charArray
	 * @param left
	 * @param right
	 * @return
	 */
	private static boolean validPalindromic(char[] charArray, int left, int right) {
		while (left < right) {
			if (charArray[left] != charArray[right]) {
				return false;
			}
			left++;
			right--;
		}
		return true;
	}

python

class Solution:
    # 暴力匹配(超时)
    def longestPalindrome(self, s: str) -> str:
        # 特判
        size = len(s)
        if size < 2:
            return s

        max_len = 1
        res = s[0]

        # 枚举所有长度大于等于 2 的子串
        for i in range(size - 1):
            for j in range(i + 1, size):
                if j - i + 1 > max_len and self.__valid(s, i, j):
                    max_len = j - i + 1
                    res = s[i:j + 1]
        return res

    def __valid(self, s, left, right):
        # 验证子串 s[left, right] 是否为回文串
        while left < right:
            if s[left] != s[right]:
                return False
            left += 1
            right -= 1
        return True

方法二:动态规划

下面是「动态规划』问题的思考路径,供大家参考。
这道题比较烦人的是判断回文子串。因此需要一种能够快速判断原字符串的所有子串是否 是回文子串的方法,于是想到了[动态规划」。
「动态规划」的一个关键的步骤是想清楚「状态如何转移」。实际上回文天然具有「状态转移」性质。

●一个文去掉两头以后,剩下的部分依然是回文(这里暂不讨论边界情况) ; 依然从回文串的定义展开讨论:
●如果一个字符串的头尾两个字符都不相等,那么这个字符串一定不是回文串;
●如果一个字符串的头尾两个字符相等, 有必要继续判断下去。
。如果里面的子串是回文,整体就是回文串;
。如果里面的子串不是回文串,整体就不是文串。

即:在头尾字符相等的情况下,里面子串的回文性质据定了整个子串的回文性质,这就是状态转移。因此可以把「状态」定义为原字符串的一一个子串是否为回文子串。
第1步:定义状态
dp[i][j]|示子串s[i…j] 否为文子串,这里子串s[i… j]义为闭右闭区间,可以取到s[i] 和s[j]。
第2步:思考状态转移方程
在这一粉类讨论(根据头尾字符是否相等), 根据上面的分析得到:

dp[i][j] = (s[i] == s[j]) and dp[i + 1][j - 1]

说明: .

「动态规划」实上是在填一张二维表格,构成子串,因此i和j的关系是i <= j,因此,只需要填这张
表格对角线以上的部分。
看到dp[i + 1][j-1]就得考虑边界情况。
边界条件是:表达式[i + 1, j- 1]不构成区间,即长度严格小于2,即j-1-(i+1)+1<2,整理得j-i <3

这个结论很显然: j 一i < 3等价于j一i + 1< 4,即当子串s[i…j]的长等于2或者等于3的时候,实
只需要判断一下头尾两个字符是否相等就可以直接 下结论了。

●如果子串s[i + 1…j- 1]只有1个字符,即去掉两头,剩下中间部分只有1个字符,显然是文;
●如果子串s[i + 1…j-1]为空串,那么子串s[i, j]| -定是回文子串。

因此,在[s[i] == s[j]| 成立和j一i < 3的前提下,直接可以下结论,dp[i][j] = true,否则才执行状态转移。
第3步:考虑初始化
初始化的时候,单个字符一定回文串,因此把对角线先初始化为true, 即dp[i][i] = true
事实上,初始化的部分都可以省去。因为只有一个字符的时候一定文,dp[i][i] 根本不会被其它状态值所参考。
第4步:考虑输出
只要一得到dp[i][j] = true ,就记录子串的长度和起始位置,没有必要截取,这是因为截取字符串也要消耗性能,记录此时的回文子串的「起始位置」和回文长度」即可。
第5步:考虑优化空间
因为在填表的过程中,只参考了左下方的数值。实上可以优化,但是增加了代码编写和理解的难度
注意事项:总是先得到小子串的回文判定,然后大子串才能参考小子串的判断结果,即填表顺序很重要。
大家能够可以自己动手,画一下表格,相信会对「动态规划」作为一种[表格法」有一个更好的理解。

java

	public static String longestPalindrome2(String s) {
		int len = s.length();
		if (len < 2) {
			return s;
		}

		int maxLen = 1;
		int begin = 0;
		boolean dp[][] = new boolean[len][len]; // 定义状态:i-j 的子串是否是回文子串

		// 每次都会检查下标是否越界,因此先转化成 字符数组
		char[] charArray = s.toCharArray();
		// 枚举所有长度大于一的子串

		for (int i = 0; i < len; i++) {
			dp[i][i] = true; // 使对角线为true,因为单个字符一定为回文字符串
		}
		for (int j = 1; j < len; j++) {
			for (int i = 0; i < j; i++) {
				if (charArray[i] != charArray[j]) {
					dp[i][j] = false;
				} else {
					if (j - i < 3) {
						dp[i][j] = true;// 长度为2的子串回文
					} else {
						dp[i][j] = dp[i + 1][j - 1];
					}
				}

				// 只要dp[i][j]==true,便记录此刻的最大深度与起始地址
				if (dp[i][j] && j - i + 1 > maxLen) {
					maxLen = j - i + 1;
					begin = i;
				}
			}
		}
		return s.substring(begin, begin + maxLen);
	}

python

class Solution1:
    def longestPalindrome(self, s: str) -> str:
        size = len(s)
        if size < 2:
            return s

        dp = [[False for _ in range(size)] for _ in range(size)]

        max_len = 1
        start = 0

        for i in range(size):
            dp[i][i] = True

        for j in range(1, size):
            for i in range(0, j):
                if s[i] == s[j]:
                    if j - i < 3:
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i + 1][j - 1]
                else:
                    dp[i][j] = False

                if dp[i][j]:
                    cur_len = j - i + 1
                    if cur_len > max_len:
                        max_len = cur_len
                        start = i
        return s[start:start + max_len]

方法三:中心扩散法

采用双指针两边夹,验证是否是回文子串。
除了枚举字符串的左右边界以外,比较容易想到的是枚举可能出现的回文子串的“中心位置”,从“中心位置”尝试尽可能扩散出去,得到一个回文串。
因此中心扩散法的思路是:遍历每一个索引,以这个索引为中心,利用“回文串”中心对称的特点,往两边扩散,看最多能扩散多远。枚举“中心位置”时间复杂度为O(N),从“中心位置扩散得到“回文子串的时间复杂度为O(N),因此时间复杂度可以降到0(N2)。.
在这里要注意一个细节: 回文串在长度为奇数和偶数的时候,“回文中心"的形式是不一样的

●奇数回文串的"中心"是一 个具体的字符,例如:文串”aba” 的中心是字符"b”;
●偶数回文串的中心是位于中间的两个字符的"空隙”,例如:文串” abba"的中心是两个”b”中间的那个空隙”。

我们可以设计一个方法,兼容以上两种情况:
1、如果传入重合的索引编码,进行中心扩散,此时得到的回文子串的长度是奇数;
2、如果传入相邻的索引编码,进行中心扩散,此时得到的回文子串的长度是偶数。
具体编码细节在以下的代码的注释中体现。

java

	public static String longestPalindrome3(String s) {
		int len = s.length();
		if (len < 2) {
			return s;
		}

		int maxLen = 1;
		String res = s.substring(0, 1);
		// 中心位置枚举到len-2即可
		for (int i = 0; i < len - 1; i++) {
			String oddStr = centerSpread(s, i, i);
			String evenStr = centerSpread(s, i, i + 1);
			String maxLenStr = oddStr.length() > evenStr.length() ? oddStr : evenStr;
			if (maxLenStr.length() > maxLen) {
				maxLen = maxLenStr.length();
				res = maxLenStr;
			}
		}
		return res;
	}

	private static String centerSpread(String s, int left, int right) {
		// TODO Auto-generated method stub
		//left==right的是时候,此时回文中心是一个字符。回文串的长度是奇数
		//right=left+1 的时候,此时回文中心是一个空隙,回文串的长度是偶数
		int len = s.length();
		int i = left;
		int j = right;
		while(i>=0&&j<len) {
			if(s.charAt(i)==s.charAt(j)){
				i--;
				j++;
			}else {
				break;
			}
		}
		//这里要小心,跳出while循环时,恰好满足s.charAt(i)!=s.charAt(j),因此不能取i,也不能取j
		
		return s.substring(i+1,j);
	}

python

class Solution2:
    def longestPalindrome(self, s: str) -> str:
        size = len(s)
        if size < 2:
            return s

        # 至少是 1
        max_len = 1
        res = s[0]

        for i in range(size):
            palindrome_odd, odd_len = self.__center_spread(s, size, i, i)
            palindrome_even, even_len = self.__center_spread(s, size, i, i + 1)

            # 当前找到的最长回文子串
            cur_max_sub = palindrome_odd if odd_len >= even_len else palindrome_even
            if len(cur_max_sub) > max_len:
                max_len = len(cur_max_sub)
                res = cur_max_sub

        return res

    def __center_spread(self, s, size, left, right):
        """
        left = right 的时候,此时回文中心是一个字符,回文串的长度是奇数
        right = left + 1 的时候,此时回文中心是一个空隙,回文串的长度是偶数
        """
        i = left
        j = right

        while i >= 0 and j < size and s[i] == s[j]:
            i -= 1
            j += 1
        return s[i + 1:j], j - i - 1

以上便是回文数的全部解法。下面是一种拓展方法,只需要了解思路就行了

方法四: Manacher 算法

Manacher算法是基于“中心扩散法",采用和kmp算法类似的思想,依然是"以空间换时间"。
Manacher算法,被中国程序员戏称为“马拉车算法。它转用于解决“最长文子串”问题,时间复杂度为O(N)。
维基百科中对于Manacher算法是这样描述的:

[Manacher(1975)]发现了一种线性时间算法,可以在列出给定字符串中从字符串头部开始的所有回文组。Apostolico, Breslauer & Galil (1995)发现,同样的算法也可以在任意位置查找全部最大回文子串,并且时间复杂度是线性的。因此,他们提供了-种时间复杂度为线性的最长回文子串解法。替代性的线性时间解决Jeuring (1994),Gusfield (1997)提供的,基于后缀树(suffix trees)。也存在已知的高效并行算法。

第1步:对原始字符串进行预处理(添加分隔符)
首先在字符串的首尾、相邻的字符中插入分隔符,例如"babad” 添加分隔符”#” 以后得到’ “#b#a#b#a#d#”。
对这-点有如下说明:

第1步:对原始字符串进行预处理(添加分隔符)
首先在字符串的首尾、相邻的字符中插入分隔符,例如"babad” 枷吩隔符”#” 以后得到” #b#a#b#a#d#”。
对这-点有如下说明:
1、分隔符是-一个字符,种类也只有一 个,并且这个字符一定不能是原始字符串中出现过的字符;
2、加入了分隔符以后,使得”间隙”有了具体的位置,方便后续的讨论,新字符串中的任意一个回文子串在原始字符串中
的- -定能找到唯-的一个回文子串与之对应,因此对新字符串的回文子串的研究就能得到原始字符串的回文子串;
3、新字符串的回文子串的长度一定是奇数;
4、新字符串的回文子串一定以分隔符作为两边的边界,因此分隔符起到哨兵”的作用。

第2步:计算辅助数组p
辅助数组p记绿了新字符串中以每个字符为中心的回子串的信息。
手动的计算方法仍然是“中心扩散法”,此时记录以当前字符为中心,向左右两边同时扩散,记绿能够扩散的最大步数。

下面是辅助数组| p的结论:辅助数组p的最大值是5,对应了原字符串” abbabb”的“最长回文子串”:。 这
个结论具有一般性,即:辅助数组 p的最大值就是“最长回文子串”的长度。
因此,我们可以在计算辅助数组p的过程中记录这个最大值,且记录最长回文子串。

最后,不经历风雨,怎能在计算机的大山之顶看见彩虹呢! 无论怎样,相信明天一定会更好!!!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

520,我会处理回文数了,你会了么?(dp+中心扩散) 的相关文章

  • 使用 JDBC 获取 Oracle 11g 的最后插入 ID

    我是使用 Oracle 的新手 所以我将放弃之前已经回答过的内容这个问题 https stackoverflow com questions 3131064 get id of last inserted record in oracle
  • Guice 忽略注入构造函数参数上的 @Nullable

    我正在使用 Guice v 3 0 并且有一个值被注入到构造函数中 该值可以为 null 因此我在构造函数中使用 Nullable 来自 javax annotations 注释了该参数 public MyClass Parameter1
  • 获取文件的锁

    我想在对特定文件开始 threo read 时获取文件上的锁定 以便其他应用程序无法读取已锁定的文件并希望在线程终止时释放锁定文件 您可以获得一个FileLock https docs oracle com javase 8 docs ap
  • Java 7 默认语言环境

    我刚刚安装了 jre7 我很惊讶地发现我的默认区域设置现在是 en US 对于jre6 它是de CH 与jre7有什么不同 默认区域设置不再是操作系统之一吗 顺便说一句 我使用的是Windows7 谢谢你的回答 编辑 我已经看到了语言环境
  • Oracle Java 教程 - 回答问题时可能出现错误

    我是 Java 新手 正在阅读 Oracle 教程 每个部分之后都有问题和答案 我不明白一个答案中的一句话 见下面的粗体线 来源是https docs oracle com javase tutorial java javaOO QandE
  • Base36 编码字符串?

    我一直在网上查找 但找不到解决此问题的方法 在 Python Ruby 或 Java 中 如何对以下字符串进行 Base 36 编码 nOrG9Eh0uyeilM8Nnu5pTywj3935kW 5 Ruby 以 36 为基数 s unpa
  • 如何使用 JAVA 代码以编程方式捕获线程转储?

    我想通过 java 代码生成线程转储 我尝试使用 ThreadMXBean 为此 但我没有以正确的格式获得线程转储 因为我们正在使用jstack命令 请任何人提供一些帮助 他们是否有其他方式获取线程转储 使用任何其他 API 我想要的线程转
  • 文本在指定长度后分割,但不要使用 grails 打断单词

    我有一个长字符串 需要将其解析为长度不超过 50 个字符的字符串数组 对我来说 棘手的部分是确保正则表达式找到 50 个字符之前的最后一个空格 以便在字符串之间进行彻底的分隔 因为我不希望单词被切断 public List
  • Spring数据中的本机查询连接

    我有课 Entity public class User Id Long id String name ManyToMany List
  • Java中的断点和逐步调试?

    抱歉我的问题名称很奇怪 我不知道如何寻找这个 因为我不知道这些东西是如何称呼的 Visual Studio 中至少有一个功能 您可以单击代码左侧并设置一个大红点的起点 然后运行程序 您可以通过按 f8 或 f5 实际上是不同的 f 来跟踪步
  • 如何通过注解用try-catch包装方法?

    如果应该在方法调用中忽略异常 则可以编写以下内容 public void addEntryIfPresent String key Dto dto try Map
  • 虽然我的类已加载,但 Class.forName 抛出 ClassNotFoundException

    代码如下 它的作用是加载我放在主目录中的 jar 文件中的所有类 import java io File import java util jar JarFile import java util jar JarEntry import j
  • 当 minifyEnabled 为 true 时 Android 应用程序崩溃

    我正在使用多模块应用程序 并且该应用程序崩溃时minifyEnabled true in the installed模块的build gradle 以下是从游戏控制台检索到的反混淆堆栈跟踪 FATAL EXCEPTION Controlle
  • Java、Spring:使用 Mockito 测试 DAO 的 DataAccessException

    我正在尝试增加测试覆盖率 所以我想知道 您将如何测试 DAO 中抛出的 DataAccessExceptions 例如在一个简单的 findAll 方法中 该方法仅返回数据源中的所有数据 就我而言 我使用 Spring JdbcTempla
  • 如何从日期中删除毫秒、秒、分钟和小时[重复]

    这个问题在这里已经有答案了 我遇到了一个问题 我想比较两个日期 然而 我只想比较年 月 日 这就是我能想到的 private Date trim Date date Calendar calendar Calendar getInstanc
  • 我们如何测试包私有类?

    我正在看书Effective Java in Item 13 Minimize the accessibility of classes and members 它提到 为了方便测试 您可能想让类 接口或成员更易于访问 这在某种程度上是好的
  • 如何停止执行的 Jar 文件

    这感觉像是一个愚蠢的问题 但我似乎无法弄清楚 当我在 Windows 上运行 jar 文件时 它不会出现在任务管理器进程中 我怎样才能终止它 我已经尝试过 TASKKILL 但它对我也不起作用 On Linux ps ef grep jav
  • 我可以限制分布式应用程序发出的请求吗?

    我的应用程序发出 Web 服务请求 提供商处理的请求有最大速率 因此我需要限制它们 当应用程序在单个服务器上运行时 我曾经在应用程序级别执行此操作 一个对象跟踪到目前为止已发出的请求数量 并在当前请求超出允许的最大负载时等待 现在 我们正在
  • 如何在Java中对对象数组进行字段级别排序以进行等级比较?

    In Java Class StudentProgress String Name String Grade CTOR goes here main class main method StudentProgress arrayofObje
  • JMS 中的 MessageListener 和 Consumer 有什么区别?

    我是新来的JMS 据我了解Consumers能够从队列 主题中挑选消息 那么为什么你需要一个MessageListener因为Consumers会知道他们什么时候收到消息吗 这样的实际用途是什么MessageListener 编辑 来自Me

随机推荐

  • android设备SD卡文件扫描与同步(暂备份)

    package com owo contentresolvermedia import java io File import java util ArrayList import android app Activity import a
  • 同一页面、不同页面监听localStorage变化

    当同源页面的某个页面修改了localStorage 其余的同源页面只要注册了storage事件 就会触发 所以 localStorage 的例子运行需要如下条件 同一浏览器打开了两个同源页面 其中一个网页修改了 localStorage 另
  • 简单易懂的隐马尔可夫模型(HMM)讲解

    学习目标 了解什么是马尔科夫链 知道什么是HMM模型 知道前向后向算法评估观察序列概率 知道维特比算法解码隐藏状态序列 了解鲍姆 韦尔奇算法 知道HMM模型API的使用 一 马尔科夫链 在机器学习算法中 马尔可夫链 Markov chain
  • Top-1错误率、Top-5错误率等常见的模型算法评估指标解析

    Top 1 错误率 指预测输出的概率最高的类别与人工标注的类别相符的准确率 就是你预测的label取最后概率向量里面最大的那一个作为预测结果 如过你的预测结果中概率最大的那个分类正确 则预测正确 否则预测错误 比如预测100张图像的类别 每
  • Spring Cloud Alibaba和Spring Cloud的区别

    目录 Spring Cloud Netflix 和 Spring Cloud 是什么关系 为什么有了Spring Cloud又出来个Spring Cloud Alibaba呢 Spring Cloud Alibaba都有哪些功能呢 Clou
  • JAVA——注解和反射

    注解的理解 引用b乎大佬的比喻 注解就像一张标签 给人贴标签是一种行为 会使一个人身上 的特性只有一部分被放大出来 但是换个角度 标签就是对事物行为的某些角度的评价与解释 从代码的角度上看 注解就是对于代码中需要拥有某些特别意义的功能的部分
  • 计算个人所得税

    输入一个职工的月薪salary 输出应交的个人所得税tax 保留2位小数 tax rate salary 850 当 salary lt 850 时 rate 0 0 当 850 lt salary lt 1350 时 rate 0 05
  • centos 6 yum源不可用安装报YumRepo Error: All mirror URLs are not using ftp, http[s] or file

    项目场景 centos6 5 使用yum安装资源时 报如下错误 1 YumRepo Error All mirror URLs are not using ftp http s or file 解决方案 修改 etc yum repos d
  • Spring Data 与MongoDB 集成四:操作篇(查询)

    本文转载至 http blog csdn net congcong68 article details 47183209 一 简介 spring Data MongoDB提供了org springframework data mongodb
  • 论文阅读:Improved Denoising Diffusion Probabilistic Models

    本文是对ddpm简单的修改 但是能提高ddpm的性能 论文下载地址 https proceedings mlr press v139 nichol21a html 我们发现反向过程中可学习的方差允许一个数量级的采样 样本质量的差异可以忽略不
  • AOP中的代理对象

    先要了解spring容器初始化过程中Bean的生命周期 如果spring在启动过程中加上了 Transiation注释的话 spring会生成一个代理对象 来做事务控制 我们从容器中取出来的对象是代理对象 代理对象在执行方法之前会开启事务管
  • 浏览器兼容性问题解决

    样式兼容性 css 方面 解决方法 可以通过 Normalize css 抹平差异 也可以定制自己的 reset css 全局重置样式 firefox浏览器不支持cursor hand显示手型 解决方法 统一使用cursor pointer
  • C# 用Microsoft.Office.Interop.PowerPoint类库操作PPT

    前言 最近由于项目需求 需要使用此类库对PPT进行操作 1 引用 Microsoft Office Interop PowerPoint和 Microsoft Office Core 2 PPT操作 打开PPT PPT应用程序变量 Appl
  • vue如何获取当前路径url及参数

    有时候开发需要获取当前url的参数 完整url可以用 window location href 路由路径可以用 this route path 路由路径参数 this route params params是参数名称 this route
  • Django-模型层(单表操作)

    目录 1 ORM简介 2 单表操作 2 1创建表 2 2添加表纪录 2 3查询表纪录 2 4删除表纪录 2 5修改表纪录 1 ORM简介 MVC或者MVC框架中包括一个重要的部分 就是ORM 它实现了数据模型与数据库的解耦 即数据模型的设计
  • 2019 前端框架对比及评测

    Jacek Schae 原作 授权 LeanCloud 翻译 我们将基于 RealWorld 示例应用对比前端框架 RealWorld 示例应用的特点 RealWorld 应用 比待办事项类应用更复杂 通常待办事项类应用不足以传达足够多的知
  • 最详细的Python接单思路和方法

    首先讲下python爬虫怎么接单挣钱 Python爬虫挣钱方法大致如下 有些需要的技术并不难 这种大部分都可以做 有些可能对技术要求比较高 门槛相对就高一点 爬虫技术挣钱方法1 接外包爬虫项目 这是网络爬虫最通常的的挣钱方式 通过外包网站
  • LeetCode-环形链表-简单

    标题 141环形链表 简单 题目 给你一个链表的头节点 head 判断链表中是否有环 如果链表中有某个节点 可以通过连续跟踪 next 指针再次到达 则链表中存在环 为了表示给定链表中的环 评测系统内部使用整数 pos 来表示链表尾连接到链
  • IDEA中配置Maven常见问题每次都需要更改setting设置,否则使用默认Maven,完美解决Maven的配置问题!

    废话不说 直接上图 你是否也遇到这种情况呢 创建maven无模板项目时 maven总是idea自带 如何解决呢 点开系统setting 1 取消默认打开上一次项目 2 重启IDEA 在全局设置就可以了 完美解决 创作不易 问题解决的给个鼓励
  • 520,我会处理回文数了,你会了么?(dp+中心扩散)

    给定一个字符串 s 找到 s 中最长的回文子串 你可以假设 s 的最大长度为 1000 示例 1 输入 babad 输出 bab 注意 aba 也是一个有效答案 示例 2 输入 cbbd 输出 bb 方法一 暴力匹配 Brute Force