NC17 最长回文子串

1 中心奇偶扩展法

  • 1.奇数长度中心扩展

    • 设置左右指针,不能超出字符串的边界
  • 1.偶数长度中心扩展

    • i,i+1为中心
    • 注意length提前定义
  • 2.遍历,res = max(odd, even res)得到最终答案

# -*- coding:utf-8 -*-

class Solution:
    def getLongestPalindrome(self, A, n):
        # write code here
        def expandOdd(i, A):
            l, r = i, i
            while l >= 0 and r < len(A):
                if A[l] == A[r]:
                    length = r - l + 1
                    l -= 1
                    r += 1
                else:
                    break
            return length
        def expandEven(i, j, A):
            l, r = i, j 
            length = 0 #这里必需要定义一下,因为,下面的循环可能不会走就return length,会报错length没有定义
            while l >=0 and r < len(A):
                if A[l] == A[r]:
                    length = r - l + 1
                    l -= 1
                    r += 1
                else:
                    break
            return length
        res = 1
        for i in range(n):
            res = max(res, expandOdd(i, A))
            res = max(res, expandEven(i, i+1, A))
        return res

2 递归

过不了

class Solution:
    def isPalindrome(self, A):
        l, r = 0, len(A)-1
        while l < r:
            if A[l] == A[r]:
                l += 1
                r -= 1
            else:
                return False
        return True

    def getLongestPalindrome(self, A, n):
        # write code here
        if self.isPalindrome(A):
            return len(A)
        return max(self.getLongestPalindrome(A[1:], n-1),self.getLongestPalindrome(A[:-1], n-1))

3 区间动态规划

dp[i,j] = (A[i] == A[j] ) and dp[i+1, j-1], j-1 > i+1

# -*- coding:utf-8 -*-

class Solution:
    def getLongestPalindrome(self, A, n ):
        if n < 2:
            return A
        max_len = 1
        begin = 0
        # dp[i][j] 表示 s[i..j] 是否是回文串
        dp = [[False] * n for _ in range(n)]
        for i in range(n):
            dp[i][i] = True
        # 递推开始
        # 先枚举子串长度
        for L in range(2, n + 1):
            # 枚举左边界,左边界的上限设置可以宽松一些
            for i in range(n):
                # 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
                j = L + i - 1
                # 如果右边界越界,就可以退出当前循环
                if j >= n:
                    break
                  
                if A[i] != A[j]:
                    dp[i][j] = False   #如果i,j对应字符不相等,那就不是回文串 
                else: 					#如果相等,直接确认是回文串
                    if j - i < 3:    
                        dp[i][j] = True
                    else:
                        dp[i][j] = dp[i + 1][j - 1]    # j-1 >= i+1 => j-i-2 >=0 => j-i >=2
                
                # 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
                if dp[i][j] and j - i + 1 > max_len:
                    max_len = j - i + 1
                    begin = i
        return max_len


<think>好的,我现在需要解决用户的问题:如何在C语言中实现NC28最小覆盖子串的算法。首先,我得理解这个问题到底要求什么。 最小覆盖子串的问题应该是给定一个字符串S和一个字符串T,找出S中包含T所有字符的最短子串。比如,如果S是"ADOBECODEBANC",T是"ABC",那么最短的子串就是"BANC"。这个问题通常可以用滑动窗口(双指针)的方法来解决,因为需要高效地扩展和收缩窗口来找到最短符合条件的子串。 接下来,我需要回忆滑动窗口算法的基本思路。大致步骤应该是这样的:用左右两个指针维护一个窗口,先移动右指针扩大窗口直到包含所有T中的字符,然后尝试移动左指针缩小窗口,同时记录最短的子串长度和起始位置。过程中需要统计字符的出现次数,可能需要哈希表或者数组来记录。 然后,我得考虑如何在C语言中实现这个算法。C语言没有内置的哈希表,所以可能需要用数组来模拟,因为ASCII字符最多256个,可以创建两个数组need和window,分别记录T中各字符的需求次数和当前窗口中的字符次数。还需要一个变量valid来统计窗口中满足需求的字符种类数。 接下来,具体步骤应该是: 1. 初始化need数组,遍历T字符串,记录每个字符的出现次数。 2. 初始化左右指针left=0, right=0,以及valid=0,记录最小窗口的起始位置和长度。 3. 移动右指针,将字符加入窗口,如果该字符在need中存在且当前窗口中的数量达到了需求,valid加1。 4. 当valid等于T中不同字符的种类数时,开始移动左指针缩小窗口,同时更新最小窗口的信息。 5. 每次移动左指针时,如果移出的字符导致窗口不再满足条件,valid减1,并继续移动右指针。 需要注意的边界情况包括:S中没有覆盖T的子串时返回空字符串,处理重复字符的情况,以及字符计数时的正确性。 另外,C语言中字符串操作需要注意结尾的'\0',比如在最后提取子串时,可以使用strndup或者动态分配内存,并复制相应的字符。 然后,我需要考虑代码的结构。首先处理特殊情况,比如T的长度为0或者S比T短,直接返回空。然后初始化两个数组,统计T的字符需求。接着滑动窗口的主体部分,左右指针移动,统计窗口中的字符,并检查是否满足条件。最后根据记录的最小窗口信息,生成结果字符串。 在代码实现中,需要注意指针越界的问题,比如right不能超过字符串长度。还有字符计数的时候,要确保数组的索引正确,比如使用字符的ASCII码作为索引。 可能遇到的错误点包括:valid的判断条件是否正确,比如只有当window[c]等于need[c]时才增加valid,而不是超过的时候;更新最小窗口时是否正确处理起始位置和长度;以及在缩小左指针时如何调整计数和valid。 测试案例方面,可以用题目中的例子,比如S="ADOBECODEBANC",T="ABC",预期得到"BANC"。另外,测试T中有重复字符的情况,比如T="AABC",此时需要确保窗口中有足够的A和B、C的数量。 最后,需要确保代码的效率和正确性,比如使用数组而不是哈希表来提高访问速度,时间复杂度应该是O(N),因为左右指针各遍历一次字符串。</think>### C语言实现NC28最小覆盖子串的解决方案 #### 算法思路 最小覆盖子串问题要求找到字符串$S$中包含字符串$T$所有字符的最短连续子串。核心解决步骤如下: 1. **滑动窗口(双指针)**:通过左右指针$left$和$right$构建窗口,逐步扩展和收缩窗口以寻找最优解[^1]。 2. **字符统计**:使用数组`need`记录$T$中每个字符的需求量,数组`window`记录当前窗口内各字符的数量。 3. **有效性判断**:通过计数器$valid$判断当前窗口是否满足$T$的全部字符需求。 #### 代码实现 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_CHAR 128 // ASCII字符范围 char* minWindow(char* s, char* t) { if (strlen(t) == 0 || strlen(s) < strlen(t)) return ""; int need[MAX_CHAR] = {0}; // 统计t中字符需求 int window[MAX_CHAR] = {0}; // 统计当前窗口字符 int required = 0; // t中不同字符的种类数 // 初始化need数组 for (int i = 0; t[i]; i++) { if (need[(int)t[i]] == 0) required++; need[(int)t[i]]++; } int left = 0, right = 0; // 滑动窗口指针 int valid = 0; // 满足需求的字符种类数 int start = 0, min_len = INT_MAX; // 最小窗口的起始位置和长度 while (right < strlen(s)) { char c = s[right]; right++; // 更新窗口统计 if (need[(int)c] > 0) { window[(int)c]++; if (window[(int)c] == need[(int)c]) { valid++; } } // 当窗口满足条件时,尝试收缩左边界 while (valid == required) { // 更新最小窗口 if (right - left < min_len) { start = left; min_len = right - left; } char d = s[left]; left++; // 更新窗口统计 if (need[(int)d] > 0) { if (window[(int)d] == need[(int)d]) { valid--; } window[(int)d]--; } } } // 构造结果字符串 if (min_len == INT_MAX) return ""; char* res = (char*)malloc(sizeof(char) * (min_len + 1)); strncpy(res, s + start, min_len); res[min_len] = '\0'; return res; } // 测试代码 int main() { char s[] = "ADOBECODEBANC"; char t[] = "ABC"; char* result = minWindow(s, t); printf("最小覆盖子串: %s\n", result); // 输出 "BANC" free(result); return 0; } ``` #### 关键点说明 1. **滑动窗口效率**:时间复杂度为$O(|S| + |T|)$,空间复杂度为$O(1)$(固定大小数组)[^1]。 2. **字符统计优化**:直接通过ASCII码索引数组,比哈希表更高效。 3. **边界处理**:当$T$长度大于$S$时直接返回空字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万物琴弦光锥之外

给个0.1,恭喜老板发财

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值