ABC214 F 题题解 + 记一次失败的比赛
1.前言
一次失败的比赛。首先为了赶时间(其实也就争取了10min左右),我尝试在出租车上打代码,下来差点吐了
q
w
q
qwq
qwq,然后车上因为晕乎乎的,把
D
D
D 和
F
F
F 的题意读错了,把
D
D
D 取
m
a
x
max
max 读成了求和(然后还嘲讽出题人只会出换根
d
p
dp
dp 板题,并大骂出题人不会出样例,浪费了
20
m
i
n
20min
20min 左右),
F
F
F 把不能留下连续的读成了不能删去连续的(然后就放弃
F
F
F, 撞死在
E
E
E 题贪心上)。总之虽然外因很多,但主要原因还是自己太菜了呀~。 不久前已经开始记录自己认为的好题,时不时重做一下吧 (虽然可能会咕)
2.题解
悲伤的回味已经过去了,现在开始进入快乐的题解部分
容易想到 d p dp dp。
状态定义:
d
p
[
i
]
dp[i]
dp[i]: 前
i
i
i 个字符中选,必选第
i
i
i 个字符,不与
s
1
⊆
s
[
1
,
i
)
s1\subseteq s_{[1,i)}
s1⊆s[1,i) 重复的满足要求的子序列个数。
状态转移:
1. ∀ j ∈ [ 1 , i ) , s [ j ] ≠ s [ i ] \forall j \in [1,i),s[j] \neq s[i] ∀j∈[1,i),s[j]=s[i]
d p [ i ] = ( ∑ k = 1 i − 2 d p [ k ] ) + 1 dp[i] = (\sum_{k = 1}^{i - 2}dp[k]) + 1 dp[i]=(∑k=1i−2dp[k])+1
2. ∃ j ∈ [ 1 , i ) , s [ j ] = s [ i ] \exists j \in [1, i), s[j] = s[i] ∃j∈[1,i),s[j]=s[i]
令 p = max j ( j ∈ [ 1 , i ) , s [ j ] = = s [ i ] ) p = \max j (j \in [1, i), s[j] == s[i]) p=maxj(j∈[1,i),s[j]==s[i])
则 d p [ i ] = ∑ k = m a x ( 1 , p − 1 ) i − 2 d p [ k ] dp[i] = \sum_{k = max (1, p - 1)}^{i - 2}{dp[k]} dp[i]=∑k=max(1,p−1)i−2dp[k]
证明:
不重:
反证法:假设会与
s
1
s1
s1 重复
1.若
k
∈
[
p
+
1
,
i
−
2
]
k \in [p + 1, i - 2]
k∈[p+1,i−2]
∵
k
∈
[
p
−
1
,
i
−
2
]
,
k
≠
p
\because k \in [p - 1, i - 2],k \neq p
∵k∈[p−1,i−2],k=p
∴
s
[
k
]
≠
s
[
i
]
\therefore s[k] \neq s[i]
∴s[k]=s[i]
与
s
1
s1
s1 重复,则
s
1
s1
s1 最后一位一定为
s
[
i
]
s[i]
s[i]
矛盾
2.若
k
=
p
k = p
k=p
d
p
[
i
]
dp[i]
dp[i] 表示的序列为
d
p
[
p
]
dp[p]
dp[p] 包含的所有序列后加一个
s
[
i
]
s[i]
s[i]。
则
s
1
s1
s1 后加一个
s
[
i
]
s[i]
s[i] 与
d
p
[
p
]
dp[p]
dp[p] 包含的某序列
(
s
2
)
(s2)
(s2)重复,且
s
t
r
str
str 是被
d
p
[
p
]
dp[p]
dp[p] 包含的一个序列。
那么,
s
2
s2
s2 的末尾有
2
2
2 个以上的
s
[
i
]
s[i]
s[i],那么
p
p
p 之前的某个位置
(
i
d
x
)
(idx)
(idx) 存在一个
s
[
i
]
s[i]
s[i]。
则
s
t
r
str
str 与
d
p
[
i
d
x
]
dp[idx]
dp[idx] 包含的序列重复(
i
d
x
idx
idx 之前(不含)的一样,
s
t
r
str
str 选择了
s
[
i
]
s[i]
s[i],与之重复的序列选择了
s
[
i
d
x
]
s[idx]
s[idx])
矛盾
3.若
k
=
p
−
1
k = p - 1
k=p−1
①若
s
[
p
−
1
]
=
s
[
i
]
s[p - 1] = s[i]
s[p−1]=s[i],和
2.
2.
2. 的分析方法一样,得出:矛盾。
②若
s
[
p
−
1
]
≠
s
[
i
]
s[p - 1] \neq s[i]
s[p−1]=s[i],
s
1
s1
s1 的末尾不为
s
[
i
]
s[i]
s[i],矛盾。
综上:矛盾
综综上:矛盾,所以不重
不漏:
d
p
[
k
]
(
k
∈
[
1
,
p
−
1
)
)
dp[k](~k\in [1, p - 1)~)
dp[k]( k∈[1,p−1) ) 包含的子序列一定会与
d
p
[
k
]
(
k
∈
[
p
−
1
,
i
−
1
)
)
dp[k](~k\in[p-1,i - 1)~)
dp[k]( k∈[p−1,i−1) )包含的子序列重复,和不重的
2.
2.
2. 分析方法一样
初始化:
d
p
[
1
]
=
1
dp[1] = 1
dp[1]=1
时间复杂度
对于每种字符,最多跑完整个序列,所以时间复杂度均摊下来 O ( 26 n ) O (26n) O(26n)
状态转移写得很清楚,代码比较简单,注意分类就行了。
(为了代码简洁,我将 d p [ 0 ] dp[0] dp[0] 置为 1 1 1,实现与状态转移的分类略有不同)
#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define PII pair <int, int>
#define LL long long
#define ULL unsigned long long
template <typename T> void read (T &x) { x = 0; T f = 1;char tem = getchar ();while (tem < '0' || tem > '9') {if (tem == '-') f = -1;tem = getchar ();}while (tem >= '0' && tem <= '9') {x = (x << 1) + (x << 3) + tem - '0';tem = getchar ();}x *= f; return; }
template <typename T> void write (T x) { if (x < 0) {x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0'); }
template <typename T> void print (T x, char ch) { write (x); putchar (ch); }
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
const int Maxn = 2 * 1e5;
const LL Mod = 1e9 + 7;
char s[Maxn + 5];
int len; LL dp[Maxn + 5];
int main () {
scanf ("%s", s + 1);
len = strlen (s + 1);
LL res = 1;
dp[1] = 1; dp[0] = 1;
for (int i = 2; i <= len; i++) {
for (int j = i - 1; j >= 1; j--) {
if (s[i] == s[j] && j - 1 == 0) break;
dp[i] = (dp[i] + dp[j - 1]) % Mod;
if (s[i] == s[j]) break;
}
res = (res + dp[i]) % Mod;
}
printf ("%lld", res);
return 0;
}