小红切字符串

[题目链接](https://www.nowcoder.com/practice/f3ff2b43d0ee4c6ca7af9e93f81d382b)

思路

将字符串切成两个非空部分,要求两部分的"权值"相等。权值定义为辅音字母数和元音字母数之差的绝对值。

前缀和 + 数学推导

给每个字符赋值:辅音为 ,元音为 。设前缀和 为前 个字符的值之和,总和为

在位置 处切割(左边 个字符,右边 个字符),两部分的权值分别为:

$$

要求 ,即 等价于

  • 情况一,即
  • 情况二,即

因此:

  • ,则所有 个切割位置都合法(因为情况二恒成立)。
  • 为奇数,则 无整数解,答案为
  • 为偶数,统计满足 的切割位置数()。

样例验证

字符串 arcaea,每个字符的值为 ,总和

前缀和:,需要 ,满足的位置为 ,答案为

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
    string s;
    cin >> s;
    int n = s.size();
    int total = 0;
    for(char c : s)
        total += (c=='a'||c=='e'||c=='i'||c=='o'||c=='u') ? -1 : 1;
    int ans = 0, pre = 0;
    for(int i = 0; i < n - 1; i++){
        pre += (s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u') ? -1 : 1;
        if(total == 0 || 2 * pre == total)
            ans++;
    }
    printf("%d\n", ans);
    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int n = s.length();
        int total = 0;
        for (int i = 0; i < n; i++)
            total += isVowel(s.charAt(i)) ? -1 : 1;
        int ans = 0, pre = 0;
        for (int i = 0; i < n - 1; i++) {
            pre += isVowel(s.charAt(i)) ? -1 : 1;
            if (total == 0 || 2 * pre == total)
                ans++;
        }
        System.out.println(ans);
    }
    static boolean isVowel(char c) {
        return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
    }
}

复杂度分析

  • 时间复杂度,一次遍历计算总和,一次遍历统计答案。
  • 空间复杂度,只用了常数个变量(不计输入存储)。