2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛 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;
}
posted @ 2023-03-15 19:21  春始于雪之下  阅读(38)  评论(0编辑  收藏  举报