leetcode1177. 构建回文串检测
1. 题目
给你一个字符串s,请你对s的子串进行检测。
每次检测,待检子串都可以表示为queries[i] = [left, right, k]。我们可以重新排列子串s[left], …, s[right],并从中选择 最多 k项替换成任何小写英文字母。
如果在上述检测过程中,子串可以变成回文形式的字符串,那么检测结果为true,否则结果为false。
返回答案数组answer[],其中answer[i]是第i个待检子串queries[i]的检测结果。
注意:在替换时,子串中的每个字母都必须作为 独立的 项进行计数,也就是说,如果s[left…right] = "aaa"且k = 2,我们只能替换其中的两个字母。(另外,任何检测都不会修改原始字符串 s,可以认为每次检测都是独立的)
示例:
输入:s = “abcda”, queries = [[3,3,0],[1,2,0],[0,3,1],[0,3,2],[0,4,1]]
输出:[true,false,false,true,true]
解释:
queries[0] : 子串 = “d”,回文。
queries[1] :子串 = “bc”,不是回文。
queries[2] :子串 = “abcd”,只替换 1 个字符是变不成回文串的。
queries[3] :子串 = “abcd”,可以变成回文的 “abba”。 也可以变成 “baab”,先重新排序变成 “bacd”,然后把 “cd” 替换为 “ab”。
queries[4] :子串 = “abcda”,可以变成回文的 “abcba”。
2. 解答
/*
注意题目的要求:1、子串重新排列2、任意修改k次以内。
因此只要统计,left到right区间内每个字符出现的次数。
*/
int** Compute(char* s, int size, int** hash)
{
for (int i = 1; i <= size; i++) {
for (int j = 0; j < 26; j++) {
hash[i][j] = hash[i - 1][j];
}
hash[i][s[i - 1] - 'a']++;
}
return hash;
}
bool Query(char* s, int left, int right, int k, int** hash)
{
int i;
int count = 0;
for (int i = 0; i < 26; i++) {
if ((hash[right + 1][i] - hash[left][i]) % 2 == 1) {
count++;
}
}
count = count / 2;
if (count > k) {
return false;
}
return true;
}
bool* canMakePaliQueries(char* s, int** queries, int queriesSize, int* queriesColSize, int* returnSize)
{
*returnSize = queriesSize;
int size = strlen(s);
int** hash = (int**)malloc(sizeof(int*) * (size + 1));
for (int i = 0; i <= size; i++) {
hash[i] = (int*)malloc(26 * sizeof(int));
memset(hash[i], 0, sizeof(int) * 26);
}
hash = Compute(s, size, hash);
bool* ans = (bool*)malloc(sizeof(bool) * queriesSize);
for (int i = 0; i < queriesSize; i++) {
bool flag = Query(s, queries[i][0], queries[i][1], queries[i][2], hash);
ans[i] = flag;
}
return ans;
}
leetcode5. 最长回文子串
1. 题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
2. 解答
回文串:如果一个字符串正着读和反着读都是一样的,就是回文串
中心扩展算法:
我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有2n-1个这样的中心。
你可能会问,为什么会是 2n - 1 个,而不是 n 个中心?
因为回文的中心要区分单双。
-
假如回文的中心为双数,例如 abba,那么可以划分为 ab bb ba,对于n长度的字符串,这样的划分有 n-1 种。
-
假为回文的中心为单数,例如 abcd, 那么可以划分为 a b c d, 对于n长度的字符串,这样的划分有 n 种。
对于 n 长度的字符串,我们其实不知道它的回文串中心倒底是单数还是双数,所以我们要对这两种情况都做遍历,也就是 n+(n-1) = 2n - 1,所以时间复杂度为 O(n)。
当中心确定后,我们要围绕这个中心来扩展回文,那么最长的回文可能是整个字符串,所以时间复杂度为 O(n)。
所以总时间复杂度为 O(n^2)
// 中心扩展法
#define MAX(a, b) (((a) > (b)) ? (a) :(b))
int expandAroundCenter(char *s, int len, int left, int right) {
int l = left;
int r = right;
while (l >= 0 && r < len && s[l] == s[r]) {
// 计算以left和right为中心的回文串长度
l--;
r++;
}
return r - l - 1;
}
char * longestPalindrome(char * s) {
int len = strlen(s);
if (len <= 1) {
return s;
}
int start = 0;
int end = 0;
int maxLen = 0;
for (int i = 0; i < len; i++) {
int len1 = expandAroundCenter(s, len, i, i); // 一个元素为中心
int len2 = expandAroundCenter(s, len, i, i + 1); // 两个元素为中心
maxLen = MAX(MAX(len1, len2), maxLen);
if (maxLen > end - start + 1) {
start = i - (maxLen - 1) / 2; // 获取回文串起始点位置
end = i + maxLen / 2; // 获取回文串结束点位置
}
}
int index = 0;
char *res = (char *)malloc(sizeof(char) * (maxLen + 1));
for (int i = start; i < start + maxLen; i++) {
res[index++] = s[i];
}
res[index] = '\0';
return res;
}