校招算法笔面试 | 华为机试-杨辉三角的变形

题目## 题目

题目链接

解题思路

  1. 这是一个变形的杨辉三角,每个数是由它上面的数和左上角、右上角的数的和构成
  2. 观察规律:
    • 第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
  3. 题目要求找第一个偶数出现的位置,如果没有偶数则返回-1
  4. 通过仔细观察可以发现:
    • 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算法。

朴素解法详解

  1. 中心扩展思想:

    • 遍历字符串的每个位置作为可能的回文中心
    • 对每个中心,分别考虑奇数长度和偶数长度的情况
    • 向两边扩展,直到不满足回文条件为止
  2. 处理两种情况:

    • 奇数长度:以当前字符为中心,如"aba"中的’b’
    • 偶数长度:以当前字符和下一个字符之间为中心,如"abba"中的"bb"之间
  3. 实现细节:

    • 使用双指针 ( l , r ) (l,r) (l,r) 向两边扩展
    • 每次扩展都更新最大长度
    • 注意边界条件的处理

Manacher算法详解

  1. 预处理字符串:

    • 在原字符串的每个字符间插入特殊字符(通常用’#')
    • 在首尾添加不同的特殊字符(如’$‘和’@'),避免边界判断
    • 例如:“abba” -> “$#a#b#b#a#@”
  2. 核心概念:

    • p [ i ] p[i] p[i]:以位置 i i i 为中心的回文半径
    • c e n t e r center center:当前最大回文串的中心位置
    • r i g h t right right:当前最大回文串的右边界
  3. 算法步骤:

    • 遍历预处理后的字符串
    • 利用已知回文信息快速跳过一些计算
    • 使用中心扩展法尽可能扩大回文半径
    • 更新 c e n t e r center center r i g h t right right
    • 维护最大回文半径
  4. 优化原理:

    • 利用回文串的对称性
    • 已计算的回文信息可以帮助计算新的位置
    • 通过right边界减少不必要的比较

示例分析

以输入 “cdabbacc” 为例:

  1. 朴素解法过程:

    • 遍历每个字符作为中心点
    • 找到 “abba” 是最长的回文子串
    • 长度为4
  2. Manacher算法过程:

    • 预处理后:$#c#d#a#b#b#a#c#c#@
    • 计算每个位置的回文半径
    • 最大回文半径对应原始字符串中的最长回文子串

算法选择建议

  1. 朴素解法:

    • 优点:实现简单,容易理解
    • 缺点:时间复杂度较高
    • 适用:字符串长度较小(如本题≤350)的情况
  2. Manacher算法:

    • 优点:线性时间复杂度,处理大规模数据高效
    • 缺点:实现复杂,需要预处理
    • 适用:字符串长度很大或需要高性能的场景

复杂度分析

  1. 朴素解法:

    • 时间复杂度: 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)
      • 只需要常量额外空间
  2. Manacher算法:

    • 时间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 虽然有两层循环,但内层循环的总执行次数是有限的
      • 每个位置最多被访问常数次
    • 空间复杂度: O ( n ) \mathcal{O}(n) O(n)
      • 需要存储预处理后的字符串
      • 需要 p p p 数组存储回文半径

代码

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) - 需要额外数组存储回文半径
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值