题目大意:有 N 个手链,每个手链的最大长度不超过100,求出来最多有多少个不同的手链。
分析:因为手链是可以转动的,所以只要两个手链通过转动达到相同,那么也被认为是一种手链,然而如果每次都循环比较的话无疑是非常浪费时间的,不过如果把每个串都用最小的字典序表示出来,那么同样的手链肯定会变成相同的状态,比如第二组数据
原串 最小表示法(字典序最小的串)
1010 --> 0101
0101 --> 0101
1000 --> 0001
0001 --> 0001
这样就比较容易判断是否相同了,可以使用字典树来判断这个串是否出现过。
代码如下:
==========================================================================================================
#include<stdio.h> #include<string.h> #include<algorithm> #include<stdlib.h> using namespace std; const int MAXM = 2; const int MAXN = 1007; struct node { node *next[MAXM]; }; bool BuildTrie(node *head, char s[]) {///建立字典树,如果s已经存在返回0,否则返回1 node *p = head; bool newNode = false; for(int i=0; s[i]; i++) { int k = s[i]-'0'; if(p->next[k] == NULL) { newNode = true; p->next[k] = new node(); } p = p->next[k]; } return newNode; } void FreeTrie(node *head) { node *p = head; for(int i=0; i<MAXM; i++) { if(p->next[i] != NULL) FreeTrie(p->next[i]); } free(p); } int GetMinOrder(char s[], int N) {///求出来最小表示的首位置 int i=0, j=1; while(i<N && j<N) { int k=0; while(s[i+k] == s[j+k] && k<N) k++; if(k == N)break; if(s[i+k] < s[j+k]) { if(j+k > i) j = j+k+1; else j = i+1; } else { if(i+k > j) i = i+k+1; else i = j+1; } } return min(i, j); } int main() { int N; while(scanf("%d", &N) != EOF) { char s[MAXN]={0}, p[MAXN]; node *head = new node(); int ans = 0, len=0; while(N--) { scanf("%s", p); if(!len)len = strlen(p); strcpy(s, p); strcat(s, p); int MinIndex = GetMinOrder(s, len); strncpy(p, s+MinIndex, len);///把这个串转换成它的最小表示 ans += BuildTrie(head, p); } printf("%d ", ans); FreeTrie(head); } return 0; }