题目链接
https://ac.nowcoder.com/acm/contest/52244/J
对于给定的字符串s我们算出它每个位置能到达的前缀最大合法位置,然后进行dp即可
先对于s串求一遍kmp,然后建立boder树,在boder树上进行倍增查找最大合法位置
因为倍增查不到会返回0,但是0号点又是boder树的根节点,所以将boder树上点+1,然后查找合法最大合法位置的时候也会从查找小于等于pos / 2的第一个点变成查找小于等于(pos + 1) / 2的第一个点,我们求出接近(pos + 1) / 2的第一个不合法位置tw,然后fa[tw][0]就是最大的合法位置,最后进行一个简单的dp即可。
#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <stack>
#include <queue>
#include <numeric>
#include <cassert>
#include <bitset>
#include <cstdio>
#include <vector>
#include <unordered_set>
#include <cmath>
#include <map>
#include <unordered_map>
#include <set>
#include <deque>
#include <tuple>
#include <array>
#define all(a) a.begin(), a.end()
#define cnt0(x) __builtin_ctz(x)
#define endl '\n'
#define itn int
#define ll long long
#define ull unsigned long long
#define rep(i, a, b) for(int i = a;i <= b; i ++)
#define per(i, a, b) for(int i = a;i >= b; i --)
#define cntone(x) __builtin_popcount(x)
#define db double
#define fs first
#define se second
#define AC main(void)
#define HYS std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
typedef std::pair<int, int > PII;
typedef std::pair<int, std::pair<int, int>> PIII;
typedef std::pair<ll, ll> Pll;
typedef std::pair<double, double> PDD;
using ld = double long;
const long double eps = 1e-9;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10, M = 4e5 + 10;
int n , m;
struct KMP{
int nxt[N];int len;
char t[N];
void clear(){
len = nxt[0] = nxt[1] =0;
}
/* 1-bas */
/* 注意在ss结尾添加‘\0’ */
void init(char* ss){
len = strlen(ss+1);
memcpy(t, ss, (len + 2) * sizeof(char));
for (int i = 2; i <= len; i ++){
nxt[i] = nxt[i-1];
while (nxt[i] && ss[i] != ss[nxt[i] + 1]) nxt[i] = nxt[nxt[i]];
nxt[i] += (ss[i] == ss[nxt[i] + 1]);
}
}
/* 求所有在ss串中的start_pos. 如果first_only设置为true,则只返回第一个位置*/
std::vector<int> match(char *ss, bool first_only = false){
int len_s = strlen(ss+1);
std::vector<int> start_pos(0);
for (int i = 1, j = 1; i <= len_s;){
while (j !=1 && ss[i] != t[j]) j = nxt[j - 1] + 1;
if (ss[i] == t[j]) j ++,i ++;
else i ++;
if (j == len + 1){
start_pos.push_back(i - j + 1);
if (first_only)return start_pos;
j = nxt[len] + 1;
}
}
return start_pos;
}
void debug(){
for (int i = 0; i <= len; i ++){
printf("[debug] nxt[%d]=%d\n", i, nxt[i]);
}
}
/* 循环周期 形如 acaca 中 ac 是一个合法周期 */
std::vector<int> periodic(){
std::vector<int> ret;
int now = len;
while (now){
now = nxt[now];
ret.push_back(len - now);
}
return ret;
}
/* 循环节 形如 acac 中ac、acac是循环节,aca不是*/
std::vector<int> periodic_loop(){
std::vector<int>ret ;
for (int x : periodic()){
if (len % x == 0) ret.push_back(x);
}
return ret;
}
int min_periodic_loop(){
return periodic_loop()[0];
}
}kmp;
int h[N], e[N], ne[N], idx, root = 1, depth[N], fa[N][21], bw = 18, q[N];
void add(int a, int b){
ne[idx] = h[a], e[idx] = b, h[a] = idx ++;
}
char str[N];
void bfs(){//初始化
for(int i = 0; i <= n + 2; i ++) depth[i] = 0x3f3f3f3f;
int hh = 0, tt = 0;
q[0] = root;//根节点入队
depth[root] = 1, depth[0] = 0;
while(hh <= tt){
int f = q[hh ++];//取出队头
for(int i = h[f]; ~i; i = ne[i]){//遍历邻边
int j = e[i];
if(depth[j] > depth[f] + 1){
depth[j] = depth[f] + 1;
q[++ tt] = j;
fa[j][0] = f;
for(int k = 1; k <= bw; k ++)
fa[j][k] = fa[fa[j][k-1]][k-1];
}
}
}
}
int LCA(int a, int b){
if(depth[a] < depth[b]) std::swap(a, b);//保持a的是深层
//让a走到和b一样深的地方去
for(int i = bw; i >= 0; i --)
if(depth[fa[a][i]] >= depth[b])
a = fa[a][i];
if(a == b) return a;//说明b是a和b的公共祖先(不一定是最近公共祖先)
//让a和b一起往上跳到最近公共祖先的先前一个点
for(int i = bw; i >= 0; i -- ){
if(fa[a][i] != fa[b][i]){
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
//本身不能是boder
int dp[N], top[N];
inline void solve(){
std::cin >> n;
std::cin >>(str + 1);
kmp.init(str);
kmp.clear();
for(int i = 0; i <= n + 2; i ++) h[i] = -1;
auto nxt = kmp.nxt;
for(int i = 1; i <= n; i ++) add(nxt[i] + 1, i + 1);
bfs();
for(int i = 2; i <= n + 1; i ++){
int j = (i + 1) / 2;
int tw = i;
for(int k = bw; ~k; k --){
if(fa[tw][k] > j) tw = fa[tw][k];
}
top[i - 1] = fa[tw][0] - 1;
}
for (int i = 1; i <= n; i++) {
if (top[i]) {
dp[i] = std::min(dp[i - 1] + 1, dp[i - top[i]] + 1);
} else {
dp[i] = dp[i - 1] + 1;
}
}
std::cout << dp[n] << '\n';
}
signed AC{
HYS
int _ = 1;
std::cin >> _;
while(_ --)
solve();
return 0;
}
标签:std,int,len,fa,上海理工大学,2023,GPLT,include,define
From: https://www.cnblogs.com/qdhys/p/17219705.html