字符串回文判断(Java/Py/C/C++/Js/Go)题解
华为OD机试真题 新系统 华为OD上机考试真题新系统 8月26号 100分题型
题目内容
给定一个仅由小写英文字母组成的字符串 sss,请判断它是否可以在删除一个字符之后,变成一个回文串。
回文串的定义是:从左到右读和从右到左读完全相同。
如果可以,输出一个数组,数组由可删除字符的索引值构成;否则输出一个空数组。
输入描述
输入一个字符串 sss,2≤s.length≤1052 \le s.length \le 10^52≤s.length≤105
输出描述
输出一个数组,内容为:NNN 种删除方法的字符索引值。如输入为:abca,则输出为:[1, 2],代表可以有两种删除方式:[aba, aca]。
1:删除索引值为1的字符,即删除字符b,则变为:aca,属于回文2:删除索引值为2的字符,即删除字符c,则变为:aba,属于回文
样例1
输入
abca
输出
1,2
说明
删除 b 或删除 c 后,都可以变成回文串。
样例2
输入
abcd
输出
说明
无论删除哪个字符,都无法变成回文串。
题解
思路:双指针 + 逻辑分析
- 分情况讨论:
- 输入本身不是回文字符串时,找到两侧第一个不相等
left和right位置,尝试任一位置字符删除之后,判断是否为回文串。 - 输入本身为回文字符串,删除一个字符后,要想仍然是回文,删除的位置必须在"中心连续相同字符区间"里。
- 输入本身不是回文字符串时,找到两侧第一个不相等
- 简单解释一下为什么回文字符串删除中心连续相同字符区间内元素还是回文串
- 回文串为奇数时,字符串关于
中心点对称,删除中心点之后肯定还是回文串 => 变为偶数回文串。要想删除位置向两侧扩展,只需要达到和删除中心点相同影响即可。而中心点两侧连续区域如果和中心点值相同,删除效果一致。 - 回文串为偶数时,字符串关于
两个中心点对称,删除任意中心点还是回文串 => 变为关于任一中心点对称的奇数回文串,要想删除位置向两侧扩展,只需要达到和删除中心点相同影响即可。而中心点两侧连续区域如果和中心点值相同,删除效果一致。
- 回文串为奇数时,字符串关于
- 基于上述分析,首先通过双指针从二侧向中心搜索,找到第一处不相同位置。如果存在不相同位置说明不是回文字符串,尝试删除左侧或右侧字符,判断是否为回文串。如果不存在相同位置,说明本身是回文串。根据2的分析,使用双指针从中心点向两侧拓展,找到
中心连续相同字符区间, 这部分区间就是结果。
C++
cpp
#include <bits/stdc++.h>
#include <vector>
using namespace std;
bool check(const string& s, int del) {
int l = 0, r = s.size() - 1;
while (l < r) {
if (l == del) {
l++;
continue;
}
if (r == del) {
r--;
continue;
}
if (s[l] != s[r]) return false;
l++;
r--;
}
return true;
}
vector<int> solve(string& s) {
int n = s.size();
vector<int> ans;
int l = 0, r = n - 1;
// 找第一处不匹配
while (l < r && s[l] == s[r]) {
l++;
r--;
}
if (l < r) {
// 原字符串不是回文,只可能删除 l 或 r
if (check(s, l)) ans.push_back(l);
if (check(s, r)) ans.push_back(r);
} else {
// 原字符串本身是回文
// 合法删除位置就是包含中心的连续相同字符区间
int left, right;
if (n % 2 == 1) {
// 奇数长度,中心是 n / 2
int mid = n / 2;
left = right = mid;
while (left > 0 && s[left - 1] == s[mid])
left--;
while (right + 1 < n && s[right + 1] == s[mid])
right++;
} else {
// 偶数长度,两个中心字符一定相等
int mid1 = n / 2 - 1;
int mid2 = n / 2;
left = mid1;
right = mid2;
while (left > 0 && s[left - 1] == s[mid1])
left--;
while (right + 1 < n && s[right + 1] == s[mid1])
right++;
}
for (int i = left; i <= right; i++)
ans.push_back(i);
}
return ans;
}
int main() {
string s;
cin >> s;
vector<int> ans = solve(s);
for (int i = 0; i < ans.size(); i++) {
if (i) cout << ",";
cout << ans[i];
}
return 0;
}
Java
java
import java.io.*;
import java.util.*;
public class Main {
static boolean check(String s, int del) {
int l = 0, r = s.length() - 1;
while (l < r) {
if (l == del) {
l++;
continue;
}
if (r == del) {
r--;
continue;
}
if (s.charAt(l) != s.charAt(r)) return false;
l++;
r--;
}
return true;
}
static List<Integer> solve(String s) {
int n = s.length();
List<Integer> ans = new ArrayList<>();
int l = 0, r = n - 1;
// 找第一处不匹配
while (l < r && s.charAt(l) == s.charAt(r)) {
l++;
r--;
}
if (l < r) {
// 原字符串不是回文,只可能删除 l 或 r
if (check(s, l)) ans.add(l);
if (check(s, r)) ans.add(r);
} else {
// 原字符串本身是回文
// 合法删除位置就是包含中心的连续相同字符区间
int left, right;
if (n % 2 == 1) {
// 奇数长度,中心是 n / 2
int mid = n / 2;
left = right = mid;
while (left > 0 && s.charAt(left - 1) == s.charAt(mid))
left--;
while (right + 1 < n && s.charAt(right + 1) == s.charAt(mid))
right++;
} else {
// 偶数长度,两个中心字符一定相等
int mid1 = n / 2 - 1;
int mid2 = n / 2;
left = mid1;
right = mid2;
while (left > 0 && s.charAt(left - 1) == s.charAt(mid1))
left--;
while (right + 1 < n && s.charAt(right + 1) == s.charAt(mid1))
right++;
}
for (int i = left; i <= right; i++)
ans.add(i);
}
return ans;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = br.readLine().trim();
List<Integer> ans = solve(s);
for (int i = 0; i < ans.size(); i++) {
if (i > 0) System.out.print(",");
System.out.print(ans.get(i));
}
}
}
Python
python
def check(s, delete):
l, r = 0, len(s) - 1
while l < r:
if l == delete:
l += 1
continue
if r == delete:
r -= 1
continue
if s[l] != s[r]:
return False
l += 1
r -= 1
return True
def solve(s):
n = len(s)
ans = []
l, r = 0, n - 1
# 找第一处不匹配
while l < r and s[l] == s[r]:
l += 1
r -= 1
if l < r:
# 原字符串不是回文,只可能删除 l 或 r
if check(s, l):
ans.append(l)
if check(s, r):
ans.append(r)
else:
# 原字符串本身是回文
# 合法删除位置就是包含中心的连续相同字符区间
if n % 2 == 1:
# 奇数长度,中心是 n / 2
mid = n // 2
left = right = mid
while left > 0 and s[left - 1] == s[mid]:
left -= 1
while right + 1 < n and s[right + 1] == s[mid]:
right += 1
else:
# 偶数长度,两个中心字符一定相等
mid1 = n // 2 - 1
mid2 = n // 2
left = mid1
right = mid2
while left > 0 and s[left - 1] == s[mid1]:
left -= 1
while right + 1 < n and s[right + 1] == s[mid1]:
right += 1
for i in range(left, right + 1):
ans.append(i)
return ans
s = input().strip()
ans = solve(s)
print(",".join(map(str, ans)), end="")
JavaScript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (s) => {
s = s.trim();
function check(s, del) {
let l = 0, r = s.length - 1;
while (l < r) {
if (l === del) {
l++;
continue;
}
if (r === del) {
r--;
continue;
}
if (s[l] !== s[r]) return false;
l++;
r--;
}
return true;
}
function solve(s) {
const n = s.length;
const ans = [];
let l = 0, r = n - 1;
// 找第一处不匹配
while (l < r && s[l] === s[r]) {
l++;
r--;
}
if (l < r) {
// 原字符串不是回文,只可能删除 l 或 r
if (check(s, l)) ans.push(l);
if (check(s, r)) ans.push(r);
} else {
// 原字符串本身是回文
// 合法删除位置就是包含中心的连续相同字符区间
let left, right;
if (n % 2 === 1) {
// 奇数长度,中心是 n / 2
const mid = Math.floor(n / 2);
left = right = mid;
while (left > 0 && s[left - 1] === s[mid])
left--;
while (right + 1 < n && s[right + 1] === s[mid])
right++;
} else {
// 偶数长度,两个中心字符一定相等
const mid1 = Math.floor(n / 2) - 1;
const mid2 = Math.floor(n / 2);
left = mid1;
right = mid2;
while (left > 0 && s[left - 1] === s[mid1])
left--;
while (right + 1 < n && s[right + 1] === s[mid1])
right++;
}
for (let i = left; i <= right; i++)
ans.push(i);
}
return ans;
}
const ans = solve(s);
console.log(ans.join(','));
rl.close();
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func check(s string, del int) bool {
l, r := 0, len(s)-1
for l < r {
if l == del {
l++
continue
}
if r == del {
r--
continue
}
if s[l] != s[r] {
return false
}
l++
r--
}
return true
}
func solve(s string) []int {
n := len(s)
ans := []int{}
l, r := 0, n-1
// 找第一处不匹配
for l < r && s[l] == s[r] {
l++
r--
}
if l < r {
// 原字符串不是回文,只可能删除 l 或 r
if check(s, l) {
ans = append(ans, l)
}
if check(s, r) {
ans = append(ans, r)
}
} else {
// 原字符串本身是回文
// 合法删除位置就是包含中心的连续相同字符区间
var left, right int
if n%2 == 1 {
// 奇数长度,中心是 n / 2
mid := n / 2
left = mid
right = mid
for left > 0 && s[left-1] == s[mid] {
left--
}
for right+1 < n && s[right+1] == s[mid] {
right++
}
} else {
// 偶数长度,两个中心字符一定相等
mid1 := n/2 - 1
mid2 := n / 2
left = mid1
right = mid2
for left > 0 && s[left-1] == s[mid1] {
left--
}
for right+1 < n && s[right+1] == s[mid1] {
right++
}
}
for i := left; i <= right; i++ {
ans = append(ans, i)
}
}
return ans
}
func main() {
in := bufio.NewReader(os.Stdin)
var s string
fmt.Fscan(in, &s)
ans := solve(s)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
for i, x := range ans {
if i > 0 {
fmt.Fprint(out, ",")
}
fmt.Fprint(out, x)
}
}
C语言
cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
bool check(const char* s, int del) {
int l = 0, r = strlen(s) - 1;
while (l < r) {
if (l == del) {
l++;
continue;
}
if (r == del) {
r--;
continue;
}
if (s[l] != s[r]) return false;
l++;
r--;
}
return true;
}
int* solve(const char* s, int* returnSize) {
int n = strlen(s);
int* ans = (int*)malloc(sizeof(int) * n);
int count = 0;
int l = 0, r = n - 1;
// 找第一处不匹配
while (l < r && s[l] == s[r]) {
l++;
r--;
}
if (l < r) {
// 原字符串不是回文,只可能删除 l 或 r
if (check(s, l))
ans[count++] = l;
if (check(s, r))
ans[count++] = r;
} else {
// 原字符串本身是回文
// 合法删除位置就是包含中心的连续相同字符区间
int left, right;
if (n % 2 == 1) {
// 奇数长度,中心是 n / 2
int mid = n / 2;
left = right = mid;
while (left > 0 && s[left - 1] == s[mid])
left--;
while (right + 1 < n && s[right + 1] == s[mid])
right++;
} else {
// 偶数长度,两个中心字符一定相等
int mid1 = n / 2 - 1;
int mid2 = n / 2;
left = mid1;
right = mid2;
while (left > 0 && s[left - 1] == s[mid1])
left--;
while (right + 1 < n && s[right + 1] == s[mid1])
right++;
}
for (int i = left; i <= right; i++)
ans[count++] = i;
}
*returnSize = count;
return ans;
}
int main() {
char s[100005];
scanf("%s", s);
int size;
int* ans = solve(s, &size);
for (int i = 0; i < size; i++) {
if (i > 0) printf(",");
printf("%d", ans[i]);
}
free(ans);
return 0;
}