「学习笔记」最小表示法

通过这个算法,我认识到了,取模,是一个神奇的东西!还有,暴力出奇迹!

——yi_fan0305的学后感

进入正题#

定义:最小表示法是用于解决字符串最小表示问题的方法。
(当我第一次学习时,我:??????,所以,别慌,看不懂定义是正常现象)


前置知识——循环同构#

对于字符串 S,有一个位置 i ,可以满足

S|i...n|+S|1...i1|=T

那么我们称 ST 循环同构,举个例子,字符串 123456789 的循环同构有 678912345234567891 等等

最小表示#

字符串 S 的最小表示就是与 S 循环同构的所有字符串中字典序最小的字符串
例如: 字符串 987654321 的最小表示为 198765432

最小表示法#

最小表示法就是找最小表示问题的算法,你可以发现,这个东西可以打暴力来解决
我们设字符串 SS 的循环同构 S1 是在 S 中以 i 为起点,循环同构 S2 是在 S 中以 j 为起点,已经判断的长度为 k,如下图
image
我们一个一个比较,如果两个位置的字符相等,++ k,继续向后搜,如果不相等,比较两个位置的大小,因为我们求的是最小表示,所以我们将大的舍弃,假设 S1i+k>S2j+k,那么我们就将 S1 舍弃,++ i,将下一个循环同构与 S2 进行比较,k 再变成 0,从头开始比较
代码

int min_show() {
	int i = 0, j = 1, k = 0;
	while (i < n && j < n && k < n) {
		if (a[(i + k) % n] == a[(j + k) % n]) {
			++ k;
		}
		else {
			if (a[(i + k) % n] > a[(j + k) % n]) {
				++ i;
			}
			else {
				++ j;
			}
			k = 0;
			if (i == j) {
				++ i;
			}
		}
	}
	return min(i, j);
}

这个暴力在一般情况下是可以应付过去的,但在一些特殊数据中,这个暴力会变成 On2 的,如 cccccccccccccccc......cccd 这样的,所以,我们要优化一下这个暴力
我们假设循环同构 S1S2 的前 p 个字符是相等的,S1 的起始点为 iS2 的起始点为 j,那么,当 S1g+1>S2g+1 时,按照暴力的操作,我们要将 i1,但是,对于以 i+1 为起点的新的循环同构,一定会有以 j+1 为起点的循环同构比它更优,这个性质在扩大一点范围,任意以 i+p 为起点的循环同构,都一定会有一个以 j+p 为起点的循环同构比它更优,前提 p[0,k],所以,我们可以将 ++ i 变为 += k + 1,即跳过 [i,i+k] 这一段
优化完后,这个暴力就变成了最小表示法了,看,暴力出奇迹
代码:

int min_show() {
	int i = 0, j = 1, k = 0;
	while (i < n && j < n && k < n) {
		if (a[(i + k) % n] == a[(j + k) % n]) {
			++ k;
		}
		else {
			if (a[(i + k) % n] > a[(j + k) % n]) {
				i += (k + 1);
			}
			else {
				j += (k + 1);
			}
			if (i == j) {
				++ i;
			}
			k = 0;
		}
	}
	return min(i, j);
}

后记#

观察这个代码,你会发现一个起始点是 0,所以你要从 0 开始输入,但由于我不习惯这种写法,所以就想方设法取模,让这个算法从 1 开始输入也能找到答案,最后,我失败了,只能说,取模是个神奇的东西
这里给出完整代码,所对应的题目是洛谷的模板题,告诉你个卡常小技巧:取模的速度其实很慢,我们可以将取模改成减法,再配合 #define 宏定义,可以提一下速度(快不了很多),下面的代码中有
代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mod(x)	(x >= n ? x - n : x)

inline ll read() {
	ll x = 0;
	int fg = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		fg |= (ch == '-');
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + (ch ^ 48);
		ch = getchar();
	}
	return fg ? -x : x;
}

const int N = 3e5 + 5;

int n;
ll a[N];

int min_show() {
	int i = 0, j = 1, k = 0;
	while (i < n && j < n && k < n) {
		int x = i + k, y = j + k;
		if (a[mod(x)] == a[mod(y)]) {
			++ k;
		}
		else {
			if (a[mod(x)] > a[mod(y)]) {
				i += k + 1;
			}
			else {
				j += k + 1;
			}
			if (i == j) {
				++ i;
			}
			k = 0;
		}
	}
	return min(i, j);
}

int main() {
	n = read();
	for (int i = 0; i < n; ++ i) {
		a[i] = read();
	}
	int ans = min_show();
	for (int i = 0; i < n; ++ i) {
		int x = i + ans;
		printf("%lld ", a[mod(x)]);
	}
	return 0;
}

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/17034093.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(126)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示