1 条题解

  • 0
    @ 2026-6-19 10:31:09

    📝 题目大意

    给定长度为 NN 的字符串 SS,定义 SiS_i 为删除 SS 的第 ii 个字符后得到的字符串。求有多少对 (i,j)(i,j) 满足 1i<jN1 \leq i < j \leq NSi=SjS_i = S_j

    💡 解题思路

    1. 题目分析:考虑 SiS_iSjS_j(设 i<ji < j)何时相等。逐字符比对:

      • 对于 k<ik < iSi[k]=SkS_i[k] = S_kSj[k]=SkS_j[k] = S_k,自然相等。
      • 对于 ik<ji \leq k < jSi[k]=Sk+1S_i[k] = S_{k+1}Sj[k]=SkS_j[k] = S_k。要使二者相等,需要 Sk+1=SkS_{k+1} = S_k 对所有 k[i,j1]k \in [i, j-1] 成立。
      • 对于 kjk \geq jSi[k]=Sk+1S_i[k] = S_{k+1}Sj[k]=Sk+1S_j[k] = S_{k+1},自然相等。

      因此 Si=SjS_i = S_j 当且仅当 Si=Si+1==SjS_i = S_{i+1} = \cdots = S_j,即 iijj 落在 SS 的同一个连续相等字符段(run)内。

    2. 算法推导:将 SS 划分为若干"极大连续相等字符段"。对于长度为 lenlen 的段,其中任意两个不同位置 (i,j)(i, j) 都满足 Si=SjS_i = S_j,贡献为组合数 C(len,2)=len(len1)2C(len, 2) = \frac{len \cdot (len-1)}{2}。遍历字符串,维护变量 a 记录当前连续相等字符的个数:

      • Si1=SiS_{i-1} = S_i,则 a++(当前段延续);
      • 否则,将当前段的贡献 a(a1)2\frac{a(a-1)}{2} 累加到答案 x,并重置 a = 1(开始新的一段)。
      • 遍历结束后,别忘了加上最后一段的贡献。
    3. 边界与细节

      • N2N \geq 2,不存在空串情况。
      • 答案可能很大:最坏情况 N=3×105N = 3 \times 10^5 且全部相同字符,答案为 N(N1)24.5×1010\frac{N(N-1)}{2} \approx 4.5 \times 10^{10},超过 32 位 int 范围,必须使用 long longstd.cppnax 均为 long long)。
      • a=1a = 1 时,a(a1)2=0\frac{a(a-1)}{2} = 0,无需特殊处理。

    ⏱️ 复杂度分析

    • 时间复杂度:仅需一次线性扫描,O(N)O(N)
    • 空间复杂度:仅使用常数个变量,O(1)O(1)

    💻 标准代码 (C++)

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        long long n, a = 1, x = 0;  // n: 字符串长度, a: 当前连续相等段长度, x: 答案
        string s;
        cin >> n >> s;
        for (int i = 1; i < n; i++) {
            if (s[i - 1] == s[i])
                a++;                // 当前段延续,长度+1
            else {
                x += a * (a - 1) / 2; // 段结束,累加 C(a, 2)
                a = 1;                // 重置为新段
            }
        }
        x += a * (a - 1) / 2;        // 处理最后一段的贡献
        cout << x;
    }
    

    信息

    ID
    867
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    (无)
    递交数
    1
    已通过
    1
    上传者