1. 程式人生 > >hdu 2609 How many(最小表示法模板+set判重)

hdu 2609 How many(最小表示法模板+set判重)

題目連結:

解題思路:

題目大意:

有n個有01組成的字串,每個字串都代表一個項鍊,那麼該字串就是一個環狀的結構,求可以經過迴圈旋轉,最後不同的串有多少個。。

演算法思想:

將每個字串轉換成最小串,然後放在set裡面去重。

最小表示法:

迴圈字串的最小表示法的問題可以這樣描述:

對於一個字串S,求S的迴圈的同構字串S’中字典序最小的一個。

由於語言能力有限,還是用實際例子來解釋比較容易:

設S=bcad,且S’是S的迴圈同構的串。S’可以是bcad或者cadb,adbc,dbca。而且最小表示的S’是adbc。

對於字串迴圈同構的最小表示法,其問題實質是求S串的一個位置,從這個位置開始迴圈輸出S,得到的S’字典序最小。

一種樸素的方法是設計i,j兩個指標。其中i指向最小表示的位置,j作為比較指標。

令i=0,j=1

如果S[i] > S[j] i=j, j=i+1

如果S[i] < S[j] j++

如果S[i]==S[j] 設指標k,分別從i和j位置向下比較,直到S[i] != S[j]
         如果S[i+k] > S[j+k] i=j,j=i+1

         否則j++
返回i

起初,我想在j指標後移的過程中加入一個優化。就是j每次不是加1,而是移動到l位置。其中,l>j且S[l]<=S[j]。但是,即使加入這一優化,在遇到bbb…bbbbbba這樣的字串時複雜度將退化到O(n^2)。

注意到,樸素演算法的缺陷在於斜體的情況下i指標的移動太少了。針對這一問題改進就得到了最小表示法的演算法。最小表示法的演算法

思路是維護兩個指標i,j。

令i=0,j=1

如果S[i] > S[j] i=j, j=i+1

如果S[i] < S[j] j++

如果S[i]==S[j] 設指標k,分別從i和j位置向下比較,直到S[i] != S[j]

         如果S[i+k] > S[j+k] i=i+k

         否則j++

返回i和j的小者

注意到上面兩個演算法唯一的區別是粗體的一行。這一行就把複雜度降到O(n)了。

AC程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <set>
#include <algorithm>
using namespace std;

const int N = 205;
int n,len;
set<string> v;
char s[N], t[N];

int minRepresstation(char *s){
    int i = 0,j = 1,k = 0;
    while(i<len && j<len && k<len){
        int tmp = s[(i+k)%len]-s[(j+k)%len];
        if(tmp == 0)
            k++;
        else{
            if(tmp > 0)
                i += k+1;
            else
                j += k+1;
            if(i == j)
                j++;
            k = 0;
        }
    }
    return min(i,j);
}

void getMin(char* str) {
    str[len/2] = '\0';
    v.insert (str);
}

int main(){
    while(~scanf("%d", &n)){
        v.clear();
        for(int i = 0; i < n; i++){
            scanf("%s",t);
            strcpy(s,t);
            strcat(s,t);
            len = strlen(s);
            int k = minRepresstation(s);
            getMin(s+k);
        }
        printf("%d\n",v.size());
    }
    return 0;
}