避重口令
小红书 9月13号 笔试真题 第一题
题目描述
短视频审核后台要把已过审成片的标题按发布时间依次拼接,得到小写字符串 SSS。SSS 的每一个子序列(含 SSS 本身与空串)都被视为已经占用的口令,不能再给新专题使用。
求最短的、不是 SSS 子序列的小写口令长度。
子序列:从原串中删除任意个(可以为零个)字符后,剩余字符保持相对顺序所形成的串。
输入描述
一行,仅含小写字母的字符串 SSS(1≤∣S∣≤1051\le |S|\le 10^51≤∣S∣≤105)。
输出描述
一行一个正整数,即最短未占用口令的长度。
样例1
输入
zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba
输出
3
说明
SSS 由两段倒序的 262626 个小写字母拼接而成。任意单个字母、任意长度为 222 的小写串都是 SSS 的子序列(前半段取第一个字符、后半段取第二个字符即可)。SSS 中字母 aaa 只出现两次,故 aaa 不是子序列,最短长度为 333。
题解和思路
思路
实现思路:动态规划
- 定义dp数组,其中
dp[i]表示从 S[i...n-1] 开始,最短的、不是 S[i...n-1] 子序列的字符串长度 - 对于当前位置i
- 如果某个字符
c在s[i...]根本不存在,那么单个字符c就已经不是子序列,所以dp[i] = 1 - 否则,选择一个字符
c,第一次匹配到它的位置是j,那么后面还需要找一个不是S[j+1...]子序列的字符串。因此dp[i] = min(d[i], 1 + dp[j] + 1)
- 如果某个字符
- 时间复杂度为
O(N)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
cin >> s;
int n = s.size();
// i之后最近c的位置
vector<vector<int>> nxt(n + 1, vector<int>(26));
for (int c = 0; c < 26; c++) {
nxt[n][c] = n;
}
for (int i = n - 1; i >= 0; i--) {
nxt[i] = nxt[i + 1];
nxt[i][s[i] - 'a'] = i;
}
const int INF = 1e9;
// 从 S[i...n-1] 开始,最短的、不是 S[i...n-1] 子序列的字符串长度
vector<int> dp(n + 1, INF);
// 空串之后,不存在任何字符可以匹配
dp[n] = 1;
for (int i = n -1; i >= 0; i--) {
for (int c = 0; c < 26; c++) {
int j = nxt[i][c];
if (j == n) {
//字符 c 在后面不存在
dp[i] = 1;
} else {
dp[i] = min(dp[i], 1 + dp[j + 1]);
}
}
}
cout << dp[0] << endl;
return 0;
}
Java
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
int n = s.length();
// i之后最近c的位置
int[][] nxt = new int[n + 1][26];
for (int c = 0; c < 26; c++) {
nxt[n][c] = n;
}
for (int i = n - 1; i >= 0; i--) {
System.arraycopy(nxt[i + 1], 0, nxt[i], 0, 26);
nxt[i][s.charAt(i) - 'a'] = i;
}
final int INF = 1000000000;
// 从 S[i...n-1] 开始,最短的、不是 S[i...n-1] 子序列的字符串长度
int[] dp = new int[n + 1];
Arrays.fill(dp, INF);
// 空串之后,不存在任何字符可以匹配
dp[n] = 1;
for (int i = n - 1; i >= 0; i--) {
for (int c = 0; c < 26; c++) {
int j = nxt[i][c];
if (j == n) {
// 字符 c 在后面不存在
dp[i] = 1;
} else {
dp[i] = Math.min(dp[i], 1 + dp[j + 1]);
}
}
}
System.out.println(dp[0]);
}
}
python
python
import sys
s = sys.stdin.readline().strip()
n = len(s)
# i之后最近c的位置
nxt = [[n] * 26 for _ in range(n + 1)]
for i in range(n - 1, -1, -1):
nxt[i] = nxt[i + 1].copy()
nxt[i][ord(s[i]) - ord('a')] = i
INF = 10**9
# 从 S[i...n-1] 开始,最短的、不是 S[i...n-1] 子序列的字符串长度
dp = [INF] * (n + 1)
# 空串之后,不存在任何字符可以匹配
dp[n] = 1
for i in range(n - 1, -1, -1):
for c in range(26):
j = nxt[i][c]
if j == n:
# 字符 c 在后面不存在
dp[i] = 1
else:
dp[i] = min(dp[i], 1 + dp[j + 1])
print(dp[0])
Javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (s) => {
s = s.trim();
const n = s.length;
// i之后最近c的位置
const nxt = Array.from({ length: n + 1 }, () => new Array(26).fill(n));
for (let c = 0; c < 26; c++) {
nxt[n][c] = n;
}
for (let i = n - 1; i >= 0; i--) {
nxt[i] = [...nxt[i + 1]];
nxt[i][s.charCodeAt(i) - 97] = i;
}
const INF = 1e9;
// 从 S[i...n-1] 开始,最短的、不是 S[i...n-1] 子序列的字符串长度
const dp = new Array(n + 1).fill(INF);
// 空串之后,不存在任何字符可以匹配
dp[n] = 1;
for (let i = n - 1; i >= 0; i--) {
for (let c = 0; c < 26; c++) {
const j = nxt[i][c];
if (j === n) {
// 字符 c 在后面不存在
dp[i] = 1;
} else {
dp[i] = Math.min(dp[i], 1 + dp[j + 1]);
}
}
}
console.log(dp[0]);
rl.close();
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var s string
fmt.Fscan(in, &s)
n := len(s)
// i之后最近c的位置
nxt := make([][]int, n+1)
for i := 0; i <= n; i++ {
nxt[i] = make([]int, 26)
for c := 0; c < 26; c++ {
nxt[i][c] = n
}
}
for i := n - 1; i >= 0; i-- {
copy(nxt[i], nxt[i+1])
nxt[i][s[i]-'a'] = i
}
const INF = int(1e9)
// 从 S[i...n-1] 开始,最短的、不是 S[i...n-1] 子序列的字符串长度
dp := make([]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = INF
}
// 空串之后,不存在任何字符可以匹配
dp[n] = 1
for i := n - 1; i >= 0; i-- {
for c := 0; c < 26; c++ {
j := nxt[i][c]
if j == n {
// 字符 c 在后面不存在
dp[i] = 1
} else {
if dp[i] > 1+dp[j+1] {
dp[i] = 1 + dp[j+1]
}
}
}
}
fmt.Fprintln(out, dp[0])
}