Leetcode 730:统计不同回文子字符串 -- C语言

2024-06-02 15:38

本文主要是介绍Leetcode 730:统计不同回文子字符串 -- C语言,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

需求

 给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模。 
 通过从 S 中删除 0 个或多个字符来获得子字符序列。 
 如果一个字符序列与它反转后的字符序列一致,那么它是回文字符序列。 
 如果对于某个  i,A_i != B_i,那么 A_1, A_2, ... 和 B_1, B_2, ... 这两个字符序列是不同的。
    
 示例 1:
 输入:
 S = 'bccb'
 输出:6
 解释:
 6 个不同的非空回文子字符序列分别为:'b', 'c', 'bb', 'cc', 'bcb', 'bccb'。
 注意:'bcb' 虽然出现两次但仅计数一次。

 示例 2: 
 输入:
 S = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba'
 输出:104860361
 解释:
 共有 3104860382 个不同的非空回文子字符序列,对 10^9 + 7 取模为 104860361。
  
 
 提示: 
 字符串 S 的长度将在[1, 1000]范围内。
 每个字符 S[i] 将会是集合 {'a', 'b', 'c', 'd'} 中的某一个。

 

思路

使用动态规划方法进行规划,我承认这个算法太TM复杂了,参考了老外的算法进行书写

  • 如果 S[i] == S[j],这时我们需要判断[i, j]这一段中有多少字符与S[i]不相等
    •     如果中间没有和S[i]相同的字母,例如"aba"这种情况,dp[i][j] = dp[i + 1][j - 1] * 2 + 2;    (dp[i][j] = dp[i + 1][j - 1] * 2 代表的dp[i + 1[j - 1]这一段可以独立存在,也可在外层包裹S[i],S[j],所有需要x2,而2是代表“aa”和“a”)
    •     如果中间只有一个和S[i]相同的字母,就是"aaa"这种情况,dp[i][j] = dp[i + 1][j - 1] * 2 + 1; (x2与上面情况相同,加一单独计算"aa",而“a”在dp[i + 1][j - 1] 中计算过了)
    •     否则中间至少有两个和S[i]相同的字母,就是"aabaa"这种情况,dp[i][j] = dp[i + 1][j - 1] * 2 - dp[left + 1][right - 1];(left、right请见代码注释,dp[left + 1][right - 1]这一段重复计算了)
  • 否则dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1];

 

代码实现

 

/*
 * 需求
 
 给定一个字符串 S,找出 S 中不同的非空回文子序列个数,并返回该数字与 10^9 + 7 的模。 
 通过从 S 中删除 0 个或多个字符来获得子字符序列。 
 如果一个字符序列与它反转后的字符序列一致,那么它是回文字符序列。 
 如果对于某个  i,A_i != B_i,那么 A_1, A_2, ... 和 B_1, B_2, ... 这两个字符序列是不同的。
    
 示例 1:
 输入:
 S = 'bccb'
 输出:6
 解释:
 6 个不同的非空回文子字符序列分别为:'b', 'c', 'bb', 'cc', 'bcb', 'bccb'。
 注意:'bcb' 虽然出现两次但仅计数一次。
 示例 2: 
 输入:
 S = 'abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba'
 输出:104860361
 解释:
 共有 3104860382 个不同的非空回文子字符序列,对 10^9 + 7 取模为 104860361。
  
 
 提示: 
 字符串 S 的长度将在[1, 1000]范围内。
 每个字符 S[i] 将会是集合 {'a', 'b', 'c', 'd'} 中的某一个。
     
 gcc countPalindromicSubsequences.c -g -o a.exe -DDEBUG
 */
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
 
#ifdef DEBUG
#define LOG(fmt, args...) fprintf(stdout, fmt, ##args)
#define BREAKER(a, b, c) breaker(a, b, c)
#else
#define LOG(fmt,...)
#define BREAKER(a, b, c)
#endif
 
#define TRUE        1
#define FALSE       0
 
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) > (b) ? (b) : (a))
 
 
int countPalindromicSubsequences(char * S){
 
    if(NULL == S){
        return 0;
    }
 
    int ** dp = NULL;
    int i = 0, j = 0;
    int size = strlen(S);
    int left = 0, right = 0;
    int ret = 0;
    int M = 1e9 + 7;
 
    dp = (int ** )malloc(size * (sizeof(int *)));
    for(i = 0; i < size; i++){
        dp[i] = (int *)malloc(size * sizeof(int)); 
    }    
 
    /*数组初始化都是0,这步骤很重要,因为dp算法,后面的计算要依赖前面的值和初始值*/
    for(i = 0; i < size; i++){
        for(j = 0; j < size; j++){
            dp[i][j] = 0;
        }
    }
 
    for(i = 0; i < size; i++){
        dp[i][i] = 1;
    }
 
    for(i = size - 2; i >= 0; i--){
        for(j = i + 1; j < size; j++){
            if(S[i] == S[j]) {
                left = i + 1;
                right = j - 1;
                while(left <= right && S[left] != S[i]){
                    left++;
                }
                while(left <= right && S[right] != S[i]){
                    right--;
                }
 
                if(left > right) { /*不包含s[i]*/
                    dp[i][j] = dp[i + 1][j - 1] * 2 + 2;
                } else if (left == right){ /*包含1个s[i]*/
                    dp[i][j] = dp[i + 1][j - 1] * 2 + 1;
                } else { /*包含2个以上s[i]*/
                    dp[i][j] = dp[i + 1][j - 1] * 2 - dp[left + 1][right - 1];
                }
            } else {
                dp[i][j] = dp[i][j - 1] + dp[i + 1][j] - dp[i + 1][j - 1];
            }
            dp[i][j] = (dp[i][j] < 0) ? dp[i][j] + M : dp[i][j] % M;
        }
    }
    
    ret = dp[0][size - 1];
    free(dp);
    dp = NULL;
    
    return ret;
}
 
 
 
void testcountPalindromicSubsequences(void){
    
    printf("\n************  testcountPalindromicSubsequences ************ \n");
    int ret = 0;
    
#if 1
 
    char * str1 = "bccb";
    ret = countPalindromicSubsequences(str1);
    printf("The Palindromic Subsequences of str %s is %d\n", str1, ret);
 
    char * str2 = "abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba";
    ret = countPalindromicSubsequences(str2);
    printf("The Palindromic Subsequences of str %s is %d\n", str2, ret);
 
#endif
 
    return; 
 
 }
 
 
 int main(int argc, char ** argv){
    testcountPalindromicSubsequences();
 }
 
 
 
 
 

代码编译

gcc countPalindromicSubsequences.c -g -o a.exe -DDEBUG

 

调试输出

************  testcountPalindromicSubsequences ************
The Palindromic Subsequences of str bccb is 6
The Palindromic Subsequences of str abcdabcdabcdabcdabcdabcdabcdabcddcbadcbadcbadcbadcbadcbadcbadcba is 104860361

这篇关于Leetcode 730:统计不同回文子字符串 -- C语言的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1024382

相关文章

Python字符串处理方法超全攻略

《Python字符串处理方法超全攻略》字符串可以看作多个字符的按照先后顺序组合,相当于就是序列结构,意味着可以对它进行遍历、切片,:本文主要介绍Python字符串处理方法的相关资料,文中通过代码介... 目录一、基础知识:字符串的“不可变”特性与创建方式二、常用操作:80%场景的“万能工具箱”三、格式化方法

浅析python如何去掉字符串中最后一个字符

《浅析python如何去掉字符串中最后一个字符》在Python中,字符串是不可变对象,因此无法直接修改原字符串,但可以通过生成新字符串的方式去掉最后一个字符,本文整理了三种高效方法,希望对大家有所帮助... 目录方法1:切片操作(最推荐)方法2:长度计算索引方法3:拼接剩余字符(不推荐,仅作演示)关键注意事

Java实现字符串大小写转换的常用方法

《Java实现字符串大小写转换的常用方法》在Java中,字符串大小写转换是文本处理的核心操作之一,Java提供了多种灵活的方式来实现大小写转换,适用于不同场景和需求,本文将全面解析大小写转换的各种方法... 目录前言核心转换方法1.String类的基础方法2. 考虑区域设置的转换3. 字符级别的转换高级转换

MySQL字符串转数值的方法全解析

《MySQL字符串转数值的方法全解析》在MySQL开发中,字符串与数值的转换是高频操作,本文从隐式转换原理、显式转换方法、典型场景案例、风险防控四个维度系统梳理,助您精准掌握这一核心技能,需要的朋友可... 目录一、隐式转换:自动但需警惕的&ld编程quo;双刃剑”二、显式转换:三大核心方法详解三、典型场景

C语言逗号运算符和逗号表达式的使用小结

《C语言逗号运算符和逗号表达式的使用小结》本文详细介绍了C语言中的逗号运算符和逗号表达式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习... 在C语言中逗号“,”也是一种运算符,称为逗号运算符。 其功能是把两个表达式连接其一般形式为:表达

Go语言实现桥接模式

《Go语言实现桥接模式》桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化,本文就来介绍一下了Go语言实现桥接模式,感兴趣的可以了解一下... 目录简介核心概念为什么使用桥接模式?应用场景案例分析步骤一:定义实现接口步骤二:创建具体实现类步骤三:定义抽象类步骤四:创建扩展抽象类步

GO语言实现串口简单通讯

《GO语言实现串口简单通讯》本文分享了使用Go语言进行串口通讯的实践过程,详细介绍了串口配置、数据发送与接收的代码实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录背景串口通讯代码代码块分解解析完整代码运行结果背景最近再学习 go 语言,在某宝用5块钱买了个

Java中的随机数生成案例从范围字符串到动态区间应用

《Java中的随机数生成案例从范围字符串到动态区间应用》本文介绍了在Java中生成随机数的多种方法,并通过两个案例解析如何根据业务需求生成特定范围的随机数,本文通过两个实际案例详细介绍如何在java中... 目录Java中的随机数生成:从范围字符串到动态区间应用引言目录1. Java中的随机数生成基础基本随

GO语言zap日志库理解和使用方法示例

《GO语言zap日志库理解和使用方法示例》Zap是一个高性能、结构化日志库,专为Go语言设计,它由Uber开源,并且在Go社区中非常受欢迎,:本文主要介绍GO语言zap日志库理解和使用方法的相关资... 目录1. zap日志库介绍2.安装zap库3.配置日志记录器3.1 Logger3.2 Sugared

Go语言中如何进行数据库查询操作

《Go语言中如何进行数据库查询操作》在Go语言中,与数据库交互通常通过使用数据库驱动来实现,Go语言支持多种数据库,如MySQL、PostgreSQL、SQLite等,每种数据库都有其对应的官方或第三... 查询函数QueryRow和Query详细对比特性QueryRowQuery返回值数量1个:*sql