给定一个长度为 n n n的串,有三种操作
求删除所有的字符 e e e的最小代价
数据范围: n ≤ 7 × 1 0 4 , 字 符 集 大 小 A ≤ 10 n\leq 7\times 10^4,字符集大小A\leq 10 n≤7×104,字符集大小A≤10
大佬题解链接
线头 d p dp dp
容易发现删除操作只会进行 n u m e num_e nume次,而且我们不可能会进行跳转到下一个 e e e的操作,因为如果这样我们还要掉头回来删掉原来那个 e e e
因此,可以发现每个 e e e右边的第一个非 e e e字符是必经字符,考虑把所有 e e e删掉,只在必经字符上进行 d p dp dp
设 f [ i ] [ j ] f[i][j] f[i][j]表示经过 [ i , i + 1 ] [i,i+1] [i,i+1]这条线段1次,跳到 j j j这个字符的最少跳跃代价
g [ i ] [ j ] [ k ] g[i][j][k] g[i][j][k]表示经过 [ i , i + 1 ] [i,i+1] [i,i+1]这条线段3次,左移前位置为 j j j,跳到 k k k这个字符的最小跳跃代价
说白了就是 f f f表示跳到 j j j, g g g表示先跳到 j j j,折返过来,再跳到 k k k
转移方程:
对于 g g g:
最后,我们知道 f f f和 g g g只和上一项相关,所以可以滚动,滚一滚一不小心内存榜第一了QwQ
时间复杂度: O ( n A 2 ) O(nA^2) O(nA2)
空间复杂度: O ( n A 2 ) O(nA^2) O(nA2)滚动后 O ( m i n ( n , A 2 ) ) O(min(n,A^2)) O(min(n,A2)),当然你也可以边输入边搞,就优化到 O ( A 2 ) O(A^2) O(A2),这基本就是一个常数了QwQ
#include
#include
#include
#define N 70010
#define A 11
using namespace std;int n,zd,gs,m;
char c;
int f[2][A],g[2][A][A],s[N];
bool need[N],p,o=1;
inline int read()
{
int d=1,f=0;char c;
while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),c>47&&c<58) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
signed main()
{
n=read();
for(register int i=1;i<=n;++i)
{
while(c=getchar(),!islower(c));
if(c=='e') zd=1,gs++;
else {need[++m]=zd;s[m]=c-'a';zd=0;}
}
for(register int i=0;i<A;++i)
{
for(register int j=0;j<A;++j) g[0][i][j]=0x3f3f3f3f;
f[0][i]=0x3f3f3f3f;
}
f[0][s[1]]=0;
for(register int i=1;i<=m;p^=1,o^=1,++i)
for(register int j=0;j<A;++j)
{
int t=0x3f3f3f3f;
if(j!=s[i]&&!need[i]) t=min(t,f[p][j]);
t=min(t,f[p][s[i]]+2);
if(j!=s[i]) t=min(t,g[p][s[i]][j]);
t=min(t,g[p][s[i]][s[i]]+2);
f[o][j]=t;
for(register int k=0;k<A;k++)
{
t=0x3f3f3f3f;
if(j!=s[i]) t=min(t,f[p][j]+3);
t=min(t,f[p][s[i]]+5);
if(j!=s[i]&&k!=s[i]) t=min(t,g[p][j][k]+1);
if(k!=s[i]) t=min(t,g[p][s[i]][k]+3);
if(j!=s[i]) t=min(t,g[p][j][s[i]]+3);
t=min(t,g[p][s[i]][s[i]]+5);
g[o][j][k]=t;
}
}
printf("%d",f[p][10]+2*gs-2);
}