题目## 题目
解题思路
- 这是一个变形的杨辉三角,每个数是由它上面的数和左上角、右上角的数的和构成
- 观察规律:
- 第1行有1个数:1
- 第2行有3个数:1 1 1
- 第3行有5个数:1 2 3 2 1
- 第4行有7个数:1 3 6 7 6 3 1
- 第5行有9个数:1 4 10 16 19 16 10 4 1
- 题目要求找第一个偶数出现的位置,如果没有偶数则返回-1
- 通过仔细观察可以发现:
- n = 1 n=1 n=1或 n = 2 n=2 n=2时,没有偶数,返回-1
- n n n为奇数时,第2个位置必定是偶数,返回2
- n n n为4的倍数时,返回3
- 其他情况( n n n为偶数但不是4的倍数),返回4
代码
#include<iostream>
using namespace std;
int main() {
int num;
while (cin >> num) {
if (num == 1 || num == 2) cout << -1 << endl;
else if (num & 1) cout << 2 << endl;
else if (num % 4) cout << 4 << endl;
else cout << 3 << endl;
}
return 0;
}
import java.util.Scanner;
public class Main {
public static int findFirstEven(int n) {
if (n == 1 || n == 2) return -1;
if (n % 2 == 1) return 2;
if (n % 4 != 0) return 4;
return 3;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
System.out.println(findFirstEven(n));
}
}
}
def find_first_even(n):
if n == 1 or n == 2:
return -1
if n % 2 == 1:
return 2
if n % 4 != 0:
return 4
return 3
while True:
try:
n = int(input())
print(find_first_even(n))
except EOFError:
break
算法及复杂度
- 算法:数学规律总结
- 时间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 直接通过判断得到结果
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 只使用常数额外空间
解题思路
这是一个寻找最长回文子串的问题。提供两种解法:朴素解法和Manacher算法。
朴素解法详解
-
中心扩展思想:
- 遍历字符串的每个位置作为可能的回文中心
- 对每个中心,分别考虑奇数长度和偶数长度的情况
- 向两边扩展,直到不满足回文条件为止
-
处理两种情况:
- 奇数长度:以当前字符为中心,如"aba"中的’b’
- 偶数长度:以当前字符和下一个字符之间为中心,如"abba"中的"bb"之间
-
实现细节:
- 使用双指针 ( l , r ) (l,r) (l,r) 向两边扩展
- 每次扩展都更新最大长度
- 注意边界条件的处理
Manacher算法详解
-
预处理字符串:
- 在原字符串的每个字符间插入特殊字符(通常用’#')
- 在首尾添加不同的特殊字符(如’$‘和’@'),避免边界判断
- 例如:“abba” -> “$#a#b#b#a#@”
-
核心概念:
- p [ i ] p[i] p[i]:以位置 i i i 为中心的回文半径
- c e n t e r center center:当前最大回文串的中心位置
- r i g h t right right:当前最大回文串的右边界
-
算法步骤:
- 遍历预处理后的字符串
- 利用已知回文信息快速跳过一些计算
- 使用中心扩展法尽可能扩大回文半径
- 更新 c e n t e r center center 和 r i g h t right right
- 维护最大回文半径
-
优化原理:
- 利用回文串的对称性
- 已计算的回文信息可以帮助计算新的位置
- 通过right边界减少不必要的比较
示例分析
以输入 “cdabbacc” 为例:
-
朴素解法过程:
- 遍历每个字符作为中心点
- 找到 “abba” 是最长的回文子串
- 长度为4
-
Manacher算法过程:
- 预处理后:$#c#d#a#b#b#a#c#c#@
- 计算每个位置的回文半径
- 最大回文半径对应原始字符串中的最长回文子串
算法选择建议
-
朴素解法:
- 优点:实现简单,容易理解
- 缺点:时间复杂度较高
- 适用:字符串长度较小(如本题≤350)的情况
-
Manacher算法:
- 优点:线性时间复杂度,处理大规模数据高效
- 缺点:实现复杂,需要预处理
- 适用:字符串长度很大或需要高性能的场景
复杂度分析
-
朴素解法:
- 时间复杂度:
O
(
n
2
)
\mathcal{O}(n^2)
O(n2)
- 遍历每个中心点: O ( n ) \mathcal{O}(n) O(n)
- 每个中心点向两边扩展: O ( n ) \mathcal{O}(n) O(n)
- 空间复杂度:
O
(
1
)
\mathcal{O}(1)
O(1)
- 只需要常量额外空间
- 时间复杂度:
O
(
n
2
)
\mathcal{O}(n^2)
O(n2)
-
Manacher算法:
- 时间复杂度:
O
(
n
)
\mathcal{O}(n)
O(n)
- 虽然有两层循环,但内层循环的总执行次数是有限的
- 每个位置最多被访问常数次
- 空间复杂度:
O
(
n
)
\mathcal{O}(n)
O(n)
- 需要存储预处理后的字符串
- 需要 p p p 数组存储回文半径
- 时间复杂度:
O
(
n
)
\mathcal{O}(n)
O(n)
代码
C++实现
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 朴素解法
class Solution1 {
public:
int longestPalindrome(string s) {
int maxLen = 1;
int n = s.length();
for(int i = 0; i < n; i++) {
// 奇数长度
int l = i, r = i;
while(l >= 0 && r < n && s[l] == s[r]) {
maxLen = max(maxLen, r - l + 1);
l--; r++;
}
// 偶数长度
l = i; r = i + 1;
while(l >= 0 && r < n && s[l] == s[r]) {
maxLen = max(maxLen, r - l + 1);
l--; r++;
}
}
return maxLen;
}
};
// Manacher算法
class Solution2 {
public:
int longestPalindrome(string s) {
string t = "$#";
for(char c : s) t += c, t += "#";
t += "@";
int n = t.size();
vector<int> p(n, 0);
int maxLen = 0;
int center = 0, right = 0;
for(int i = 1; i < n-1; i++) {
if(i < right) p[i] = min(right - i, p[2 * center - i]);
while(t[i + p[i] + 1] == t[i - p[i] - 1]) p[i]++;
if(i + p[i] > right) {
center = i;
right = i + p[i];
}
maxLen = max(maxLen, p[i]);
}
return maxLen;
}
};
int main() {
string s;
cin >> s;
Solution1 sol1;
// Solution2 sol2;
cout << sol1.longestPalindrome(s) << endl;
return 0;
}
import java.util.*;
public class Main {
// 朴素解法
static class Solution1 {
public int longestPalindrome(String s) {
int maxLen = 1;
int n = s.length();
for(int i = 0; i < n; i++) {
// 奇数长度
int l = i, r = i;
while(l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
maxLen = Math.max(maxLen, r - l + 1);
l--; r++;
}
// 偶数长度
l = i; r = i + 1;
while(l >= 0 && r < n && s.charAt(l) == s.charAt(r)) {
maxLen = Math.max(maxLen, r - l + 1);
l--; r++;
}
}
return maxLen;
}
}
// Manacher算法
static class Solution2 {
public int longestPalindrome(String s) {
StringBuilder t = new StringBuilder("$#");
for(char c : s.toCharArray()) {
t.append(c).append("#");
}
t.append("@");
String str = t.toString();
int n = str.length();
int[] p = new int[n];
int maxLen = 0;
int center = 0, right = 0;
for(int i = 1; i < n-1; i++) {
if(i < right) p[i] = Math.min(right - i, p[2 * center - i]);
while(str.charAt(i + p[i] + 1) == str.charAt(i - p[i] - 1)) p[i]++;
if(i + p[i] > right) {
center = i;
right = i + p[i];
}
maxLen = Math.max(maxLen, p[i]);
}
return maxLen;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
Solution1 sol1 = new Solution1();
// Solution2 sol2 = new Solution2();
System.out.println(sol1.longestPalindrome(s));
}
}
Python实现
# 朴素解法
def longest_palindrome1(s: str) -> int:
max_len = 1
n = len(s)
for i in range(n):
# 奇数长度
l, r = i, i
while l >= 0 and r < n and s[l] == s[r]:
max_len = max(max_len, r - l + 1)
l -= 1
r += 1
# 偶数长度
l, r = i, i + 1
while l >= 0 and r < n and s[l] == s[r]:
max_len = max(max_len, r - l + 1)
l -= 1
r += 1
return max_len
# Manacher算法
def longest_palindrome2(s: str) -> int:
t = '$#' + '#'.join(s) + '#@'
n = len(t)
p = [0] * n
max_len = 0
center = right = 0
for i in range(1, n-1):
if i < right:
p[i] = min(right - i, p[2 * center - i])
while t[i + p[i] + 1] == t[i - p[i] - 1]:
p[i] += 1
if i + p[i] > right:
center = i
right = i + p[i]
max_len = max(max_len, p[i])
return max_len
# 主程序
s = input().strip()
print(longest_palindrome1(s)) # 使用朴素解法
# print(longest_palindrome2(s)) # 使用Manacher算法
算法及复杂度
朴素解法
- 时间复杂度: O ( n 2 ) \mathcal{O}(n^2) O(n2) - 每个中心点向两边扩展
- 空间复杂度: O ( 1 ) \mathcal{O}(1) O(1) - 只需要常量额外空间
Manacher算法
- 时间复杂度: O ( n ) \mathcal{O}(n) O(n) - 线性时间复杂度
- 空间复杂度: O ( n ) \mathcal{O}(n) O(n) - 需要额外数组存储回文半径
182

被折叠的 条评论
为什么被折叠?



