1 条题解

  • 0
    @ 2025-12-4 19:03:04

    一、详细思路分析

    1. 问题核心:判断区间字符重排后能否构成回文

    一个字符串重新排列后能构成回文的核心条件

    • 若区间长度为偶数:所有字符的出现次数必须是偶数(每个字符都能成对出现,对称排列);
    • 若区间长度为奇数:恰好有1个字符的出现次数是奇数(作为回文中心),其余均为偶数。 统一结论:区间内出现次数为奇数的字符数量 ≤ 1 时,可构成回文;否则不能。

    2. 高效区间查询:前缀和数组

    由于方块数量 ( N ) 和查询次数 ( Q ) 均高达 ( 10^5 ),若每次查询都遍历区间统计字符次数,时间复杂度为 ( O(Q*N) ),会超时。因此需要前缀和预处理

    • 定义二维数组 ( s[26][maxn] ),其中 ( s[j][i] ) 表示前 ( i ) 个方块中,第 ( j ) 个字母(( j=0 ) 对应 'A',( j=1 ) 对应 'B',…,( j=25 ) 对应 'Z')的出现次数。
    • 预处理时,遍历每个位置 ( i ),对每个字母 ( j ):
      • 若当前字符是 ( j ) 对应的字母,则 ( s[j][i] = s[j][i-1] + 1 );
      • 否则 ( s[j][i] = s[j][i-1] )(继承前 ( i-1 ) 个的计数)。
    • 查询区间 ([L, R]) 时,第 ( j ) 个字母的出现次数 = ( s[j][R] - s[j][L-1] ),时间复杂度 ( O(1) ) 每字母。

    3. 整体流程

    1. 输入处理:读取 ( N, Q ) 和字符字符串;
    2. 前缀和预处理:构建26个字母的前缀和数组;
    3. 处理每个查询
      • 读取区间 ( L, R );
      • 遍历26个字母,计算区间内每个字母的出现次数,统计奇数次数的字母数量;
      • 若奇数次数的字母数 ≤ 1,输出 "YES";否则输出 "NO"。

    4. 时间复杂度分析

    • 预处理:( O(N26) ),( N=10^5 ) 时,261e5=2.6e6,无压力;
    • 查询:( O(Q*26) ),( Q=10^5 ) 时,同样是2.6e6次操作,总时间复杂度 ( O((N+Q)*26) ),完全满足题目要求。

    二、带详细注释的代码

    #include<iostream>
    using namespace std;
    
    // 定义数组最大长度,适配题目中N≤1e5的限制
    const int maxn = 100010;
    
    // str存储输入的字符字符串(下标从1开始,方便前缀和计算)
    char str[maxn];
    // s[j][i]:前i个字符中,第j个字母(j=0→A,j=1→B…j=25→Z)的出现次数
    int s[26][maxn];
    // n:方块数量,m:查询数量(代码中用m代替题目中的Q)
    int n, m;
    
    int main() {
        // 读取方块数量n和查询数量m
        cin >> n >> m;
        // 读取字符串,str+1表示从数组下标1开始存储(避免下标0的前缀和处理麻烦)
        scanf("%s", str + 1);
    
        // 预处理前缀和数组
        for(int i = 1; i <= n; i ++) { // 遍历每个字符位置(1~n)
            for(int j = 0; j < 26; j ++) { // 遍历26个大写字母
                // 判断当前位置i的字符是否是第j个字母(j+'A'转换为对应字符)
                if(str[i] == j + 'A') {
                    // 若是,当前字母计数 = 前i-1个的计数 + 1
                    s[j][i] = s[j][i - 1] + 1;
                } else {
                    // 若不是,继承前i-1个的计数
                    s[j][i] = s[j][i - 1];
                }
            }
        }
    
        // 处理每个查询
        while(m --) {
            int l, r; // 当前查询的区间[L, R]
            cin >> l >> r;
            int cnt = 0; // 统计区间内出现次数为奇数的字母数量
    
            // 遍历26个字母,计算每个字母在[l, r]区间的出现次数
            for(int i = 0; i < 26; i ++) {
                // 区间次数 = 前r个的计数 - 前l-1个的计数
                int sum = s[i][r] - s[i][l - 1];
                // 若次数为奇数,计数+1
                if(sum % 2 == 1) cnt ++;
            }
    
            // 判断是否满足回文条件:奇数次数的字母数≤1
            if(cnt <= 1) {
                cout << "YES" << endl;
            } else {
                cout << "NO" << endl;
            }
        }
    
        return 0;
    }
    

    三、代码关键细节补充

    1. 字符串下标从1开始:前缀和数组 ( s[j][0] ) 默认为0(前0个字符无字母),若字符串从0开始,计算 ( [l, r] ) 时需调整为 ( s[j][r+1] - s[j][l] ),容易出错,因此选择从1开始更直观。
    2. scanf读取字符串:相比cin,scanf读取字符数组效率更高,适合处理1e5长度的字符串。
    3. 奇偶性判断:无需关心字母具体出现次数,只需判断奇偶性(因为偶数次可成对,奇数次仅允许1个),因此直接对 sum % 2 即可。

    四、测试用例验证

    以题目输入为例:

    7 5
    ABAACCA
    3 6 → 字符为AACC → 次数:A=2(偶)、C=2(偶)→ cnt=0 → YES
    4 4 → 字符为A → 次数:A=1(奇)→ cnt=1 → YES
    2 5 → 字符为BAAC → 次数:B=1(奇)、A=2(偶)、C=1(奇)→ cnt=2 → NO
    6 7 → 字符为CA → 次数:C=1(奇)、A=1(奇)→ cnt=2 → NO
    3 7 → 字符为AACCA → 次数:A=3(奇)、C=2(偶)→ cnt=1 → YES
    

    输出与题目一致,验证代码正确性。

    • 1

    信息

    ID
    3811
    时间
    1000ms
    内存
    128MiB
    难度
    5
    标签
    (无)
    递交数
    114
    已通过
    41
    上传者