USACO Training Section 1.3 Greedy Algorithm

本文通过修复牛栏的实际问题介绍了贪心算法的基本思想。该算法通过局部最优选择来实现全局最优,并以N块木板修复连续牛栏为例进行讲解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

贪心算法

例题:修复牛栏(1999年USACO春季公开赛)

现在我们有一排牛栏,但其中的一些需要修复(就是拿块木板去修理),你最多能使用N(1 ≤ N ≤ 50)块木板,其中每一块木板可以搞定连续的Mi个牛栏。你的任务是使用最少的木板数搞定所有需要修复的牛栏。

想法:

贪心算法最基本的思路是“从一个大型问题较小的部分开始解决,一步一步以至于完成整个问题”(编者说一句:这里我的理解是假如有规模为N的问题,我们可以先试着解决规模为1时的最优情况。接下来在1最优的基础上,加上第2部分…直到把N部分处理完毕)。然而,和其他方法不同的是,贪心算法在每一步的计算中只采用仅对这一步来说最优的方案,而非环顾全局。所以,如果用上面的例题做例子的话,贪心算法在计算N = 5时的方案时,会先了解并沿用N = 4时的最优方案,然后在N = 4最优方案的基础上计算得到N = 5时的最优方案。在这里,N = 4时的除了最优方案之外的任何方案都没有被考虑在内(都被永远的丢掉了)。

贪心算法非常,时间复杂度

<think>题目要求:在给定的字符串中找出最长的回文子串(忽略非字母字符,但输出时要保留原字符)。注意:回文串在原文中是连续的(包括空格和标点,但在判断回文时忽略非字母,只考虑字母的对称)。同时,如果有多个相同长度的最长回文,输出最先出现的。 思路: 1. 由于字符串长度最大20000,但回文串长度(仅考虑字母)不超过2000,我们可以考虑使用中心扩展法(因为2000个字符的回文,中心最多40000个,但实际我们只考虑字母,所以中心点会减少,但20000字符的字符串,中心点最多20000个,扩展总次数不超过20000*(回文长度/2)),而回文长度不超过2000,所以最坏情况是20000*1000=20e6,在C++中可接受。 2. 但是,题目要求输出原串中的回文(包括非字母字符),所以我们需要记录回文在原串中的起始和结束位置。 3. 具体步骤: a. 预处理:将原字符串保存下来,同时创建一个新的字符串,只包含字母(并转换为同一大小写,比如大写),并记录每个字母在原字符串中的位置索引(这样我们可以在新串中扩展得到的回文,映射回原串的位置)。 b. 使用中心扩展法遍历新串(即只包含字母的串)的每个位置(考虑奇数和偶数回文): - 对于每个中心,向两边扩展,直到不是回文为止。 - 记录扩展过程中得到的回文长度和对应的原串的起始和结束位置(通过之前记录的位置索引映射)。 c. 注意:中心扩展法需要分别考虑奇数长度(中心为一个字符)和偶数长度(中心为两个字符之间)的情况。 d. 在扩展过程中,我们只考虑新串(字母串)中的字符,但记录下当前回文在新串中对应的起始和结束索引,然后通过映射得到原串的起始和结束索引。 e. 由于题目要求如果有多个回文长度都等于最大值,输出最前面出现的那一个,所以当我们找到一个更长的回文时,就更新答案(记录原串的起始和结束位置),而相同长度时,由于我们是从左到右遍历,所以第一次遇到的最长回文就是最前面的。 4. 输出:输出回文长度(即新串中回文的字母个数),然后输出原串中从起始位置到结束位置的子串(包括其中的非字母字符)。 5. 注意:原串中的换行符需要保留,所以输出时不能改变原串的格式。 实现细节: - 预处理:遍历原串,对于每个字符,如果是字母,则将其转换为大写(或小写)添加到新串,并记录该字符在原串中的索引(位置)。非字母则跳过。 - 中心扩展:对于新串的每个位置i(0~n-1),进行两次扩展: 1. 以i为中心,向两边扩展(奇数) 2. 以i和i+1之间的空隙为中心(偶数,即从i和i+1开始向两边扩展) - 扩展时,比较新串中左右字符是否相等(因为已经统一大小写,所以直接比较即可),直到不相等或越界。 6. 例子:样例输入 "Confucius say: Madam, I&#39;m Adam." 预处理后,新串为:"CONFUCIUSSAYMADAMIMADAM" (注意去掉标点空格),但是实际记录的位置索引是原串中每个字母的位置。 其中回文部分对应原串中的 "Madam, I&#39;m Adam",注意这个子串包含空格和标点,但新串中我们只取字母部分判断回文。 7. 如何映射回原串? 假设在新串中我们得到一个回文,起始索引为L(在新串中),结束索引为R(在新串中)。那么原串中对应的起始位置就是我们在预处理时记录的位置数组中的第L个位置,结束位置就是位置数组中的第R个位置。这样我们就得到了原串中回文子串的起始和结束下标(两个下标之间的子串,包括中间的所有字符)。 8. 注意:原串中可能有换行,所以我们在读取输入时,要保留所有字符(包括换行)。但是题目说每行不超过80个字符(不包括换行符),总字符数不超过20000。 9. 输入:由于可能有多行,我们可以用getline逐行读取,直到EOF,然后拼接成一个字符串(保留换行符)?或者直接逐行读入,然后存储在一个字符串中,每行后面加上读入的换行符(但注意最后一行可能没有换行符?题目没有明确,但样例是一行)。根据题目,每行不超过80字符(不包括换行符),所以我们可以按行读入,然后每行后面加上一个换行符(除了最后一行可能不加?)。但是题目要求输出时如果回文包含换行符,那么输出也要多行。所以我们需要保留原输入的所有换行符。 10. 因此,我们读取输入时,可以: string input = ""; string line; while (getline(cin, line)) { input += line + "\n"; // 这样每行后面都加了换行符 } 但是这样最后一行会多一个换行符。如果输入的最后一行有换行符,那么这样就会多一个。所以我们需要判断是否最后一行有换行符?实际上,题目没有说明,但USACO的输入文件通常以换行符结束。但题目没有明确,所以我们可以不添加最后的换行符?但是题目说“每行都不超过80个字符(不包括最后的换行符)”,说明每行都有换行符(除了最后一行可能没有?)。所以为了安全,我们可以: string input; char c; while (cin.get(c)) { input += c; } 这样原样保留所有字符(包括换行符)。 11. 预处理:我们遍历input字符串,对于每个字符,判断是否是字母(isalpha),如果是,则将其转换为大写(或小写)加入新串clean,同时记录该字符在input中的位置(即下标)到数组pos中。 12. 中心扩展算法: int start = 0, end = 0; // 记录当前最长回文在新串中的起始和结束索引(在clean串中) int maxLen = 0; int n = clean.size(); vector<int> pos; // 记录clean中每个字符在input中的位置 for (int i = 0; i < n; i++) { // 奇数情况 int l = i, r = i; while (l >= 0 && r < n && clean[l] == clean[r]) { if (r - l + 1 > maxLen) { maxLen = r - l + 1; start = l; end = r; } l--; r++; } // 偶数情况 l = i, r = i+1; while (l >= 0 && r < n && clean[l] == clean[r]) { if (r - l + 1 > maxLen) { maxLen = r - l + 1; start = l; end = r; } l--; r++; } } 但是这样会重复计算,而且效率不是最优(虽然最坏2000*1000=2e6,但实际中心扩展次数是O(n^2)?但题目说回文长度不超过2000(字母个数),所以每次扩展最多扩展1000次(因为一半最多1000),而中心点有n个(最多20000),所以总操作次数最多20000*2000=40e6,在C++中可能勉强通过(但题目总字符数20000,但clean串可能很长,但题目保证回文长度不超过2000,所以实际扩展次数不会超过2000次每个中心?所以总操作次数是20000*2000=40e6,在C++中可能超时?因为40e6次操作在1秒内可能有点紧,但USACO的机器比较老,所以需要优化?) 13. 优化:实际上,在扩展过程中,当扩展的长度已经不可能超过当前最大长度时,可以跳过?但这样需要记录当前最大长度,然后计算当前中心能扩展的最大可能长度。但这样并不好优化。或者我们可以使用更高效的方法:Manacher算法?但Manacher算法可以O(n)时间,但是题目要求回文长度不超过2000,所以中心扩展法40e6次操作在C++中可能勉强通过(因为USACO Training的机器可能比较慢,但1.3节的题目一般数据不大,而且这里总操作次数40e6,在C++中通常1秒内可以完成,但为了保险,我们可以用Manacher算法?) 14. 但是题目要求输出原串中的回文(包括非字母),而Manacher算法只能给出新串中的最长回文,而且需要记录位置。所以同样需要预处理的位置映射。而且Manacher算法稍微复杂。 15. 权衡:由于题目保证回文长度(字母个数)不超过2000,所以中心扩展法在扩展时,每个中心最多扩展1000次(因为回文长度不超过2000,所以半径最多1000),而中心点有n个(最多20000),所以总扩展次数最多20000*1000=20e6(因为每次扩展一步,每步比较两个字符,所以总比较次数20e6*2=40e6,在C++中,40e6次比较可能刚好在1秒内完成(在USACO的老机器上可能有点紧,但题目是1.3节,应该可以接受)。 16. 但是,我们可以在扩展过程中,如果当前中心的最大可能回文长度(即从中心到边界的距离的两倍)已经小于当前最大长度,就可以跳过?但是这样并不容易,因为边界是不规则的(左右边界到中心的距离不同)。所以我们可以不优化,直接做。 17. 注意:在扩展过程中,我们记录的是在新串clean中的回文区间[start, end],然后通过pos[start]和pos[end]得到原串中的起始位置和结束位置。然后输出原串中从pos[start]到pos[end]的子串。 18. 但是,如果有多个相同长度的回文,我们要求最前面出现的,所以当我们找到一个和当前最大长度相同的回文时,不需要更新(因为我们是按顺序遍历的,所以第一次遇到的最大长度就是最前面的)。 19. 但是,上面的中心扩展代码中,我们每次更新都是当回文长度大于当前最大长度时才更新,所以相同长度时不会更新,因此我们记录的是第一次遇到的最大长度回文。但是注意,我们遍历中心是从左到右,所以相同长度时,先遇到的中心扩展出来的回文(在clean串中)一定比后遇到的更靠前(在clean串中,也就是原串的字母顺序更靠前)。但是原串中回文的起始位置是由pos[start]决定的,而start是clean串中的索引,由于clean串中的字符在原串中出现的顺序就是pos数组中的顺序,所以clean串中索引小的对应的原串位置也小。因此,我们这样记录的回文子串在原串中也是最先出现的。 20. 但是,注意:同一个回文串可能被多次扩展?比如,一个长的回文串,它的中心可能被多次扩展,但我们在扩展过程中,当长度大于当前最大长度时才更新,所以不会重复记录相同长度。而且我们要求最前面出现的那一个,所以当我们第一次遇到一个长度为L的回文时,就记录下来了,后面再遇到相同长度的就不会更新。 21. 因此,我们按照上述中心扩展法实现。 22. 边界情况:当输入为空时,输出0和一个空行。 23. 代码步骤: 步骤1:读取整个输入(保留所有字符,包括换行)。 步骤2:预处理,得到clean串和pos数组。 步骤3:初始化maxLen=0,start_index=0, end_index=0(用于记录clean串中的回文区间)。 步骤4:遍历clean串的每个位置,进行奇数和偶数扩展。 步骤5:扩展过程中,记录回文长度,如果大于当前最大长度,则更新maxLen和start_index, end_index(在clean串中的索引)。 步骤6:扩展结束后,根据pos数组,得到原串中的起始位置p1=pos[start_index],结束位置p2=pos[end_index]。 步骤7:输出maxLen(即回文串的字母个数),然后输出原串中从p1到p2的子串(包括中间的所有字符)。 24. 注意:样例输出中,回文串是"Madam, I&#39;m Adam",注意原串中这个子串的起始位置和结束位置。另外,样例输出第一行是11(因为回文串的字母个数是11:M,A,D,A,M,I,M,A,D,A,M,注意大小写忽略,但实际我们统计的是字母个数,所以是11)。 25. 样例验证:输入"Confucius say: Madam, I&#39;m Adam.",预处理后clean串为"CONFUCIUSSAYMADAMIMADAM",然后中心扩展时,会在中间的&#39;MADAMIMADAM&#39;部分扩展,但最长回文应该是"MADAMIMADAM"(11个字母),对应原串中的"Madam, I&#39;m Adam"(注意原串中这个子串的位置)。 26. 但是,注意原串中"MADAM"后面有空格和逗号,然后"I&#39;m"中间有标点,但我们在clean串中只保留了字母,所以扩展时不会受到标点影响。 27. 实现代码: 注意:由于原串中可能有多个连续的非字母,但clean串中只保留字母,所以clean串中相邻的两个字母在原串中可能隔着多个非字母。但我们的pos数组记录的是每个字母在原串中的位置,所以当我们得到clean串的回文区间[start_index, end_index]时,原串中对应的子串就是input.substr(pos[start_index], pos[end_index]-pos[start_index]+1)。 28. 但是,注意:原串中,两个字母之间可能有换行符,所以输出时也要换行。例如,如果回文跨越了换行符,那么输出时也要包含换行符。 29. 因此,我们直接输出原串中从pos[start_index]到pos[end_index]的子串即可。 30. 代码: 由于题目要求输出格式:第一行是回文长度(字母个数),第二行开始是回文原文(可能包含多行)。 31. 注意:原串中可能有换行符,所以输出时不能使用cout<<input.substr(...)<<endl; 因为这样会破坏原有换行。直接输出子串,子串中包含了换行符。 32. 但是,样例输出是两行:第一行是11,第二行是"Madam, I&#39;m Adam"(没有句号,因为原文中句号在回文之后?)。注意原文是:"Confucius say: Madam, I&#39;m Adam.",回文子串是从&#39;M&#39;开始到&#39;m&#39;结束(不包括最后的句号),所以是"Madam, I&#39;m Adam"(注意原串中这个子串的结束位置是最后一个&#39;m&#39;的位置,不是句号)。 33. 所以,我们的方法正确。 34. 代码实现: 注意:如果整个字符串都没有字母,那么clean为空,此时maxLen=0,然后我们输出0,然后输出空行?但是原串中可能有非字母,但题目要求回文必须是字母,所以没有字母时,最长回文长度为0,然后输出空行(但原串非空,我们输出什么?题目要求输出回文的原文,所以是空串。但注意,题目要求输出回文原文,所以如果回文长度为0,那么原文就是空串,所以第二行输出空行?但题目要求输出下一行或几行包括这个回文,所以长度为0时,我们输出0,然后输出一个空行?但是原串中可能有非字母,但回文必须至少有一个字母?题目没有明确,但根据定义,回文至少一个字母(因为非字母被忽略,所以没有字母就不构成回文)。所以当没有字母时,最长回文长度为0,然后我们输出0和一个空行。 35. 另外,如果回文长度为1,那么任何一个字母都是回文。 36. 代码: 由于输入可能包含多行,我们使用: string input; char c; while (cin.get(c)) { input += c; } 预处理: string clean; vector<int> pos; // 记录clean中每个字符在input中的下标 for (int i = 0; i < input.size(); i++) { if (isalpha(input[i])) { clean += toupper(input[i]); // 转换为大写,方便比较 pos.push_back(i); } } 中心扩展: int n = clean.size(); int start = 0, end = 0; // 在clean中的位置 int maxLen = 0; for (int i = 0; i < n; i++) { // 奇数扩展 for (int j = 0; i-j>=0 && i+j<n; j++) { if (clean[i-j] != clean[i+j]) break; int len = 2*j+1; if (len > maxLen) { maxLen = len; start = i-j; end = i+j; } } // 偶数扩展 for (int j = 0; i-j>=0 && i+j+1<n; j++) { if (clean[i-j] != clean[i+j+1]) break; int len = 2*j+2; if (len > maxLen) { maxLen = len; start = i-j; end = i+j+1; } } } 但是,上面的循环中,我们每次j从0开始,然后逐次扩展,这样写起来简单,但和之前思路一样。 37. 输出: cout << maxLen << endl; if (maxLen > 0) { int left = pos[start]; int right = pos[end]; cout << input.substr(left, right - left + 1) << endl; } else { // 如果maxLen=0,说明没有回文(即没有字母),那么输出空行 cout << endl; } 38. 但是,注意:题目要求如果有多个回文长度都等于最大值,输出最前面出现的那一个。上面的代码在找到更大的长度时才更新,所以相同长度时不会更新,因此我们记录的是第一次出现的最长回文。但是,有可能在遍历过程中,先遇到一个较短的回文,然后后面遇到一个更长的,然后更新,但相同长度时不会覆盖,所以符合要求。 39. 但是,上面的中心扩展循环中,我们并没有提前终止,而是每个中心都扩展到底。由于回文长度不超过2000,所以每个中心最多扩展1000次,总扩展次数最多20000*1000=20e6,可以接受。 40. 但是,我们还可以优化:在扩展过程中,如果当前扩展的半径已经不可能超过当前最大长度的一半,就可以提前终止?但是这样优化效果不大,而且会复杂。所以我们先按照这样写。 41. 测试样例: 输入: "Confucius say: Madam, I&#39;m Adam." 预处理后clean: "CONFUCIUSSAYMADAMIMADAM" 在clean中,最长的回文应该是"MADAMIMADAM"(11个字母),位置是从第12个字符(0-indexed)开始?我们来看: clean: C O N F U C I U S S A Y M A D A M I M A D A M 索引0开始:C -> 中心在中间字母?比如中心在第一个&#39;M&#39;(索引12)?实际上,最长的回文中心在中间的&#39;I&#39;(索引18)?不对,应该是从第15个字母(M)开始,到第25个字母(M)结束?我们数一下: M A D A M I M A D A M -> 11个字母 在clean中,这个子串的起始索引是15(M),结束索引是25(M)?但是clean中第15个字母是第一个M(在"AY"后面的M)吗?注意原串是"Madam, I&#39;m Adam",对应clean中应该是:M(位置13),A(14),D(15),A(16),M(17),I(18),M(19),A(20),D(21),A(22),M(23) -> 所以结束位置是23,起始位置是13,长度11。 所以,中心扩展时,我们以索引18(即&#39;I&#39;)为中心(奇数扩展): j=0: i=18, 比较18和18 -> 相等(M? 不对,中心是&#39;I&#39;,所以第一次比较的是中心自己,然后j=1: 比较17和19 -> M和M相等,j=2: 16和20 -> A和A相等,j=3: 15和21 -> D和D相等,j=4:14和22->A和A相等,j=5:13和23->M和M相等,j=6:12和24->A和? 24超出范围?不对,clean串长度只有24(0-23)?所以j=6时,24超出范围,所以回文长度是11(j=0到j=5,半径5,长度11)。 所以,中心扩展时,以18为中心,可以扩展出11的回文。 42. 因此,输出maxLen=11,然后原串中从pos[13]到pos[23]的子串。 43. 原串中,我们记录的位置: input: "Confucius say: Madam, I&#39;m Adam." (注意后面有句号,但clean中不包括句号) clean中第0个字符是&#39;C&#39;,在input中位置0。 ... clean中第13个字符是第一个&#39;M&#39;(在"Madam"的第一个M),在input中的位置是13(因为"Confucius say: "一共13个字符:注意"Confucius"9个字符," say: "有5个字符,但中间有一个空格,所以:C(0) o(1) n(2) f(3) u(4) c(5) i(6) u(7) s(8) (空格9) s(10) a(11) y(12) :(13) 然后空格(14) -> 所以第一个&#39;M&#39;在位置15?不对,我们数一下: "Confucius say: " C o n f u c i u s 空格 s a y : 空格 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -> 所以第14个位置是空格,然后下一个字符是&#39;M&#39;(位置15)。 所以clean中第13个字符(即第一个M)在input中的位置是15(因为前面有14个字符,从0到14,所以第15个位置是M)。 然后clean中第23个字符(最后一个M)在input中的位置:原串中"Madam, I&#39;m Adam"的最后一个M,前面有:Madam, I&#39;m Ada -> 共15-25(包括逗号空格等): M(15), a(16), d(17), a(18), m(19), ,(20), 空格(21), I(22), &#39;(23), m(24), 空格(25), A(26), d(27), a(28), m(29) -> 所以最后一个M在位置29?但是clean中只记录字母,所以clean中第23个字符(最后一个M)在input中位置是29。 所以输出原串中从位置15到29的子串:input.substr(15, 29-15+1) = "Madam, I&#39;m Adam"(注意最后没有句号,因为句号在位置30,不包括)。 44. 所以输出为: 11 Madam, I&#39;m Adam 45. 但是,样例输出是: 11 Madam, I&#39;m Adam 注意:样例输出没有句号,所以正确。 46. 但是,我们输出时,最后没有换行符?原串中最后一个字符是句号,我们输出的子串不包括句号,但包括换行符吗?样例输入是一行,所以没有换行符。我们读入时,如果输入文件最后没有换行,那么input字符串最后就没有换行符。 47. 因此,我们按照上述代码实现。 48. 注意:由于输入可能很大(20000字符),所以使用getline或cin.get()读取整个输入时,要确保不会超时(不会,因为20000字符)。 49. 另外,使用vector和string拼接,空间足够。 50. 代码: 注意:在中心扩展时,我们使用了两层循环,内层循环j表示扩展的半径(步长)。这样写比较简单。 51. 但是,上面的中心扩展代码中,奇数扩展和偶数扩展是分开的,并且每个中心都扩展到底。我们也可以写成函数,但为了清晰,直接写。 52. 最终代码: 注意:在USACO中,文件输入输出,所以需要重定向。 但是题目没有要求,我们只写核心代码,然后提交到USACO系统时,需要加上文件重定向。 53. 完整代码: 由于题目要求,我们写完整的程序。 54. 考虑一个特殊情况:输入字符串为空。 55. 代码:</think>### 解决思路 要解决最长回文子串问题,需要在一个包含标点、空格和换行符的字符串中,找到最长的回文子串(忽略非字母字符,但输出时保留原字符)。核心思路如下: 1. **预处理输入字符串**: - 提取所有字母字符并转换为统一大小写(这里用大写),同时记录每个字母在原字符串中的位置。 - 这样可以在处理回文时忽略非字母字符,但能通过位置映射回原字符串。 2. **中心扩展算法**: - 遍历预处理后的字母串的每个字符,分别以每个字符为中心(奇数长度回文)和每两个字符之间的空隙为中心(偶数长度回文)向两边扩展。 - 扩展过程中比较左右字符是否相等,记录最大回文长度及其在原串中的起止位置。 3. **处理多个最长回文**: - 题目要求输出最先出现的最长回文,因此只在找到更长的回文时更新结果,相同长度时不更新。 4. **输出结果**: - 第一行输出最长回文的字母个数。 - 第二行输出原字符串中对应的子串(包含非字母字符和换行符)。 ### 代码实现 ```cpp #include <iostream> #include <cctype> #include <vector> #include <cstring> #include <string> using namespace std; int main() { string input; char c; while (cin.get(c)) { input += c; } string clean = ""; vector<int> pos; for (int i = 0; i < input.size(); i++) { if (isalpha(input[i])) { clean += toupper(input[i]); pos.push_back(i); } } int n = clean.size(); int maxLen = 0; int start_clean = 0, end_clean = 0; for (int i = 0; i < n; i++) { // 奇数长度回文,中心为i int l = i, r = i; while (l >= 0 && r < n && clean[l] == clean[r]) { int len = r - l + 1; if (len > maxLen) { maxLen = len; start_clean = l; end_clean = r; } l--; r++; } // 偶数长度回文,中心为i和i+1之间 l = i; r = i + 1; while (l >= 0 && r < n && clean[l] == clean[r]) { int len = r - l + 1; if (len > maxLen) { maxLen = len; start_clean = l; end_clean = r; } l--; r++; } } cout << maxLen << endl; if (maxLen > 0) { int start_orig = pos[start_clean]; int end_orig = pos[end_clean]; cout << input.substr(start_orig, end_orig - start_orig + 1); } else { // 处理无字母的情况 cout << endl; } return 0; } ``` ### 代码解释 1. **输入处理**: - 使用`cin.get(c)`逐字符读取输入,保留所有字符(包括换行符)。 2. **预处理**: - `clean`字符串存储所有字母的大写形式。 - `pos`向量记录每个字母在原始输入中的位置。 3. **中心扩展算法**: - **奇数扩展**:以当前字符为中心,向左右扩展直到不匹配。 - **偶数扩展**:以当前字符和下一个字符之间的空隙为中心,向左右扩展。 - 每次找到更长的回文时,更新`maxLen`和在`clean`中的起止索引。 4. **输出**: - 通过`pos`向量将`clean`中的索引映射回原始输入的位置。 - 使用`substr`输出原始回文字串(包含非字母字符)。 ### 样例验证 输入:`"Confucius say: Madam, I&#39;m Adam."` - `clean`字符串:`"CONFUCIUSSAYMADAMIMADAM"` - 最长回文:`"MADAMIMADAM"`(长度11) - 原始子串:`"Madam, I&#39;m Adam"` 输出: ``` 11 Madam, I&#39;m Adam ``` ### 注意事项 - 算法时间复杂度为$O(n^2)$,但题目限定回文长度不超过2000,实际运行效率可以接受。 - 正确处理了多行输入和换行符。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值