1 条题解

  • 0
    @ 2025-8-24 21:34:55

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Siyuan
    Dream OIer.

    搬运于2025-08-24 21:34:55,当前版本为作者最后更新于2019-02-08 11:19:36,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    $$\Large\texttt{My Blog}$$


    Description

    题目链接:Luogu 2187

    小 Z 的有一个长度为 nn 的笔记,他规定有 mm 个字母对不能相邻,并且是与字母顺序无关的。现在给出这个笔记,请求出最少需要擦掉多少个字母,使得整个笔记合法。

    数据范围:1n1051\le n\le 10^51m4001\le m\le 400


    Solution

    我们可以很容易地写出朴素的 DP\text{DP} 转移方程:

    $$f_i=\min\{f_j+i-j-1\}\quad(0\le j<i,\text{第}\ i\ \text{个字母和第}\ j\ \text{个字母可以相邻}) $$

    显然整个转移方程的复杂度为 O(n2)O(n^2),我们考虑如何优化。首先我们把带有 ii 的项提出,得到 fi=min{fjj}+i1f_i=\min\{f_j-j\}+i-1,那么这个方程的实质就是求出最小的 fjjf_j-j。我们对于每个字母 ii 维护一个 fiif_i-i 的最小值 gig_i,每次枚举上一个字母 kk,用 gk+i1g_k+i-1 来更新 fif_i 即可。

    时间复杂度O(26n)O(26\cdot n)


    Code

    #include <cstdio>
    #include <algorithm>
    
    const int N=1e5+5;
    int n,f[N],g[30];
    char s[N];
    bool e[30][30];
    
    char getc() {
    	char c=getchar();
    	while(c<'a'||c>'z') c=getchar();
    	return c;
    }
    int main() {
    	scanf("%d%s",&n,s+1);
    	int m;
    	for(scanf("%d",&m);m--;) {
    		int u=getc()-'a',v=getc()-'a';
    		e[u][v]=e[v][u]=1;
    	}
    	for(int i=1;i<=n;++i) {
    		f[i]=1<<30;
    		for(int j=0;j<26;++j) if(!e[s[i]-'a'][j]) f[i]=std::min(f[i],g[j]+i-1);
    		g[s[i]-'a']=std::min(g[s[i]-'a'],f[i]-i);
    	}
    	printf("%d\n",f[n]);
    	return 0;
    }
    
    • 1

    信息

    ID
    1170
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    (无)
    递交数
    0
    已通过
    0
    上传者