用法:
一个首位相连的字符串,我们要寻找一个位置,从这个位置向后形成一个新字符串,我们需要使这个字符串字典序最小。
算法:
有一个字符串str,下标为 0 ~ len
我们这里要i = 0,j = 1,k = 0,表示从i开始k长度和从j开始k长度的字符串相同(i,j表示当前判断的位置)
(1) 当str[i + k] == str[j + k] 时 k++;
(2) 当str[i] > str[j]时,我们发现i位置比j位置上字典序要大,那么不能使用i作为开头了,我们要将i向后移动,移动多少呢?有因为i开头和j开头的有k个相同的字符,那么就执行 i = i + k +1;
(3) 反之则 j = j + k + 1;
(4) 最终i和j中较小的值就是我们最终开始的位置
相反如果是最大表示法的话,我们就要求解字典序最大的字符串,那么我们只需要在执行第二或第三个操作时选择较大的那个位置较好了
HDU 2609 题目链接
题意
给出n个0/1串,通过循环可以相同的串算一类,求一共有几类串。
思路
用最小表示法表示字符串,然后用set去重即可
#include <cstring>
#include <string>
#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
using namespace std;
const int N = 1e4 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
typedef long long ll;
char str[N];
set<string> st;
int get_min()
{
int i = 0,j = 1,k = 0;
int len = strlen(str);
while(i < len && j < len && k < len){
if (str[(i + k) % len] == str[(j + k) % len]) k++;
else {
if (str[(i + k) % len] > str[(j + k) % len]) i = i + k + 1;
else j = j + k + 1;
if (i == j) j++;
k = 0;
}
}
return min(i,j);
}
int main()
{
int n;
while(scanf("%d",&n) == 1){
st.clear();
for (int i = 0; i < n; i++){
scanf("%s",str);
int x = get_min();
string s = "";
int len = strlen(str);
for (int i = x; i < len + x; i++){
s += str[i % len];
}
st.insert(s);
}
cout << st.size() << endl;
}
return 0;
}