循环同构
定义:如果存在整数 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[i…j]表示字符串
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[j…j+k]的字典序是比 S [ i … i + k ] S[i\dots i+k] S[i…i+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 k≥n或i≥n或j≥n时,循环停止, 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 eg:aa…aab,时间复杂度就成 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+2、i+3…i+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