How many
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 6487 Accepted Submission(s): 2983
Problem Description
Give you n ( n < 10000) necklaces ,the length of necklace will not large than 100,tell me
How many kinds of necklaces total have.(if two necklaces can equal by rotating ,we say the two necklaces are some).
For example 0110 express a necklace, you can rotate it. 0110 -> 1100 -> 1001 -> 0011->0110.
Input
The input contains multiple test cases.
Each test case include: first one integers n. (2<=n<=10000)
Next n lines follow. Each line has a equal length character string. (string only include ‘0’,‘1’).
Output
For each test case output a integer , how many different necklaces.
有一个首位相连的字符串,我们要寻找一个位置,从这个位置向后形成一个新字符串,我们需要使这个字符串字典序最小。
算法解释
我们这里要i = 0,j = 1,k = 0,表示从i开始k长度和从j开始k长度的字符串相同(i,j表示当前判断的位置)
当我们str[i] == str[j]时,根据上面k的定义,我们的需要进行k+1操作
当str[i] > str[j]时,我们发现i位置比j位置上字典序要大,那么不能使用i作为开头了,我们要将i向后移动,移动多少呢?有因为i开头和j开头的有k个相同的字符,那么就执行 i = i + k +1
相反str[i] < str[j]时,执行:j = j + k +1
最终i和j中较小的值就是我们最终开始的位置
相反如果是最大表示法的话,我们就要求解字典序最大的字符串,那么我们只需要在执行第二或第三个操作时选择较大的那个位置较好了
Sample Input
4
0110
1100
1001
0011
4
1010
0101
1000
0001
Sample Output
1
2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <set>
using namespace std;
string str;
int len;
int getmin(){
int i=0,j=1,k=0;// i , j 表示位置,k 表示两个位置之后相同字符的个数
while(i<len&&j<len&&k<len){
int t=str[(i+k)%len]-str[(j+k)%len];//比较两个位置字符的大小
if(t==0)k++;
else{
if(t>0)//最小表示法
i+=k+1;//j+=k+1,最大表示法(注释处)
else
j+=k+1;//i+=k+1
if(i==j)j++;
k=0;
}
}
return i>j ? j : i;
}
int main()
{
int n;
while(cin >> n){
set<string>q;
q.clear();
for(int i=0;i<n;i++){
cin >> str;
len=str.size();
str+=str;//在两个字符串中一定可以截取最小字典树的字符串
int t=getmin();//得到最小字典树的开始位置
q.insert(str.substr(t,len));
}
cout << q.size() << endl;
}
return 0;
}