最小表示法详解

循环同构

定义:如果存在整数 k k k ,使得串 S S S 在循环右移 k k k位置之后变得等于串 T T T ,则称串 S S S T T T循环同构

最小表示法

定义:字符串 S S S的最小表示法为与串 S S S所有循环同构的字符串中字典序最小的字符串

求解算法

有字符串 S S S,其长度为 n n n S [ i ] S[i] S[i]表示字符串第 i i i个字符, S [ i … j ] S[i\dots j] S[ij]表示字符串 S S S从下标 i i i到下标 j j j的子串
算法思路:

  • i i i j j j表示串 S S S两个循环同构的串的开头, k k k代表对比到的字符
  • 如果 S [ i + k ] = S [ j + k ] S[i+k]=S[j+k] S[i+k]=S[j+k],就++k继续比下一个字符
  • 如果 S [ i + k ] > S [ j + k ] S[i+k]>S[j+k] S[i+k]>S[j+k],说明 S [ j … j + k ] S[j\dots j+k] S[jj+k]的字典序是比 S [ i … i + k ] S[i\dots i+k] S[ii+k]小的,就改变 + + i ++i ++i
  • 反之则 + + j ++j ++j
  • 算法运行过程中, i i i j j j不能相等,当 i i i j j j相等时就要错开
  • k ≥ n 或 i ≥ n 或 j ≥ n k\ge n或i\ge n或j\ge n kninjn时,循环停止, m i n ( i , j ) min(i,j) min(i,j)就是字典序最小的循环同构串的开头对应的下标

C o d e Code Code

int minNotatin(vector<int>& s) {
	int k = 0, i = 0, j = 1, n = s.size();
	while (k < n && i < n && j < n) {
		if (s[(i + k) % n] == s[(j + k) % n])++k;//相同就递增k
		else (s[(i + k) % n] > s[(j + k) % n] ? ++i : ++j), k = 0;//不同就改变i或j的值
		if (i == j)++i;//当i=j时将二者错开
	}
	return min(i, j);
}

时间复杂度分析,一般情况,序列随机时间复杂度为 O ( n ) O(n) O(n),最坏情况,字符串S有大量相同元素,但不是全相同,使得 k k k每次遍历,都能到最大值, e g : a a … a a b eg:aa\dots aab egaaaab,时间复杂度就成 O ( n 2 ) O(n^2) O(n2)

优化
优化思路

这里的优化类似字符串 K M P KMP KMP算法。我假设此时 S [ i + k ] > S [ j + k ] S[i+k]> S[j+k ] S[i+k]>S[j+k],在区间 [ i , i + K ] [i,i+K] [i,i+K]内,就不会有最终的答案,我们就可以直接将 i i i移到 i + k + 1 i+k+1 i+k+1的位置, S [ i + k ] < S [ j + k ] S[i+k]< S[j+k] S[i+k]<S[j+k]时同理,就是将 j j j移动到 j + k + 1 j+k+1 j+k+1

证明

那为什么在 [ i , i + K ] [i,i+K] [i,i+K]内,就不会有最终的答案呢?
我没可以画一个图来理解,如下图

如果我们选择 i + 1 i+1 i+1位置为下一个循环同构串的开头,那一定存在以 j + 1 j+1 j+1为开头的循环同构串是比前者更优的,因为 [ i + 1 , i + k ] [i+1,i+k] [i+1,i+k] [ j + 1 , j + k ] [j+1,j+k] [j+1,j+k]两个区间是完全相同的,且 S [ i + k ] > S [ j + k ] S[i+k]> S[j+k] S[i+k]>S[j+k],以此类推 i + 2 、 i + 3 … i + k i+2、i+3\dots i+k i+2i+3i+k都不是最终的答案,所以就可以直接跳到 i + k + 1 i+k+1 i+k+1的位置

C o d e Code Code

int minNotatin(string& s) {
	int k = 0, i = 0, j = 1, n = s.size();
	while (k < n && i < n && j < n) {
		if (s[(i + k) % n] == s[(j + k) % n])++k;
		else (s[(i + k) % n] > s[(j + k) % n] ? i = i + k + 1 : j = j + k + 1), k = 0; //不相等时将i或j跳到下一个位置
		if (i == j)++i;//当i=j时将二者错开
	}
	return min(i, j);
}

这样时间复杂度就是 O ( n ) O(n) O(n)

练习

这里有几道运用最小表示法的习题,来练习一下吧!
P1368 【模板】最小表示法
Cyclically Isomorphic-HDU

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IdlePerson.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值