C语言实现含a/b字符串三等分最大可能性求解技术问询
解决思路与C语言实现
首先,你的方向是对的——总'a'的数量必须能被3整除是核心前提,否则直接返回0。递归在这里其实不是最优解,因为我们不需要实际划分字符串,只需要统计符合条件的划分点数量,用线性遍历+统计的方法更高效,也更容易理清边界条件。
核心逻辑拆解
我们分三种情况处理:
- 字符串长度不足3:直接返回0,因为无法分成三个非空部分。
- 总'a'数为0(全是'b'):此时问题转化为在
n-1个间隙中选2个作为划分点,组合数公式是C(n-1, 2) = (n-1)*(n-2)/2,比如输入"bbbbb"(n=5),计算得4*3/2=6,和示例一致。 - 总'a'数是3的倍数(设为3k,k>0):我们需要找到两个关键的划分范围:
- 第一部分:从字符串开头到某个位置
i,恰好包含k个'a',且i后面至少留2个字符(给第二、第三部分)。 - 第二部分:从
i+1到某个位置j,恰好包含k个'a',且j后面至少留1个字符(给第三部分)。
总划分数 = 第一部分合法i的数量 × 第二部分合法j的数量。
- 第一部分:从字符串开头到某个位置
具体实现步骤
基础版本(嵌套遍历,易理解)
先写一个容易理解的版本,用嵌套遍历统计合法划分点:
#include <stdio.h> #include <string.h> int countPartitions(char *s) { int len = strlen(s); if (len < 3) return 0; // 统计总'a'的数量 int total_a = 0; for (int i = 0; i < len; i++) { if (s[i] == 'a') total_a++; } // 总'a'数不能被3整除,直接返回0 if (total_a % 3 != 0) return 0; // 全是'b'的情况,计算组合数 if (total_a == 0) { return (len - 1) * (len - 2) / 2; } int k = total_a / 3; int total = 0; int current_a = 0; // 遍历第一个划分点的所有可能位置 for (int i = 0; i < len - 2; i++) { if (s[i] == 'a') current_a++; // 当第一部分恰好有k个'a'时,统计第二部分的合法划分点 if (current_a == k) { int second_a = 0; int second_count = 0; for (int j = i + 1; j < len - 1; j++) { if (s[j] == 'a') second_a++; if (second_a == k) { second_count++; } } total += second_count; } } return total; } // 测试示例 int main() { printf("%d\n", countPartitions("ababa")); // 输出4 printf("%d\n", countPartitions("bbbbb")); // 输出6 printf("%d\n", countPartitions("ababb")); // 输出0 return 0; }
优化版本(线性时间,高效)
对于长字符串,嵌套遍历效率较低,我们可以提前记录所有'a'的位置,直接计算合法划分点的范围:
#include <stdio.h> #include <string.h> #include <stdlib.h> int countPartitions(char *s) { int len = strlen(s); if (len < 3) return 0; int total_a = 0; for (int i = 0; i < len; i++) { if (s[i] == 'a') total_a++; } if (total_a % 3 != 0) return 0; if (total_a == 0) { return (len - 1) * (len - 2) / 2; } int k = total_a / 3; // 记录所有'a'的索引位置 int *a_indices = (int*)malloc(total_a * sizeof(int)); int idx = 0; for (int i = 0; i < len; i++) { if (s[i] == 'a') { a_indices[idx++] = i; } } // 计算第一部分合法i的数量:从第k个'a'的前一个位置到第k-1个'a'的位置 int first_start = a_indices[k-1]; int first_end = a_indices[k] - 1; // 确保i后面至少留2个字符 if (first_end > len - 3) first_end = len - 3; int first_count = first_end - first_start + 1; // 计算第二部分合法j的数量:从第2k个'a'的前一个位置到第2k-1个'a'的位置 int second_start = a_indices[2*k -1]; int second_end = a_indices[2*k] - 1; // 确保j后面至少留1个字符 if (second_end > len - 2) second_end = len - 2; int second_count = second_end - second_start + 1; free(a_indices); return first_count * second_count; } int main() { printf("%d\n", countPartitions("ababa")); // 输出4 printf("%d\n", countPartitions("bbbbb")); // 输出6 printf("%d\n", countPartitions("ababb")); // 输出0 return 0; }
为什么不推荐递归?
递归的思路会让你陷入"划分后处理子串"的循环,但实际上我们只需要统计划分点的数量,不需要实际拆分字符串。递归不仅会带来额外的栈开销,还容易在边界条件(比如字符串长度不足、划分点位置合法判断)上出错,不如直接统计高效清晰。
内容的提问来源于stack exchange,提问作者Ryan
相关产品推荐
相关产品推荐

