計蒜客 旋轉數字

2021-10-04 01:21:10 字數 4609 閱讀 8227

計蒜客 - 旋轉數字

蒜頭君發現了乙個很好玩的事情,他對乙個數作旋轉操作,把該數的最後的數字移動到最前面。比如,數 123123 可以得到 312, 231, 123312,231,123,這樣就可以得到很多個數。

現在,蒜頭君的問題是這些數中,有多少個不同的數小於原數,多少個等於原數,多少個大於原數。

旋轉中可能會出現前導零,兩數比較的時候可以忽略前導零的影響。

輸入格式

輸入乙個整數 n(0

10100000

)n(0 < n\leq 10^)n(0

1010

0000

)。

341
輸出格式

答案在一行中輸出三個整數,分別是小於 n,等於 n,大於 n 的個數,中間以空格隔開。

1 1 1
這道題是一道利用拓展 kmp 演算法的好題目。

我們一點一點來剖析這道題。

第乙個問題,怎麼列舉出所有旋轉得到的數字?

把數字收尾相接,然後從第 0 個位置到第 length 個位置,每次往後取 length 個長度的字元,就可以遍歷出所有可能的 length 個數字。

char num[max_len]

;scanf

("%s"

, num)

;int len =

strlen

(num)

;char

* numnum =

duplicate

(num)

;for

(int i =

0; i < len; i++

)printf

("\n");

}

char

*concat

(const

char

* s1,

const

char

* s2)

char

*duplicate

(const

char

* s)

第二個問題,怎麼比較大小?

由於數字非常大,我們不可能每次都轉成整型以後去比較大小,而利用字串相等來比較數字之間的大小,是一種常用的做法。

首先讓我們考慮怎麼比較兩個數字相等,顯然,那就是這兩個字串相等,即strcmp(s1, s2) == 0,對應到 kmp 演算法中,那就是next[s[i]] = length

parseint

(s1)

===parseint

(s2)

// 等價於

s1 === s2

當然,這裡忽略了前導零,因為s1 = '001's2 = '01',顯然parseint(s1) === parseint(s2)是成立的,但是s1 === s2是不成立的。

可是,由於題目要求的是不同的數字,所以我們不需要考慮重複數字,那麼,在這種情況下,與原數相等的數字就只有 1 個了。

所以我們只需要比較大於,或者小於原數的數字。

給定兩個字串s1s2,如果已知他們開始的x個字元都相等,如何判斷它們的大小?只需要比較第x + 1個位置上的字元的大小就可以了。

而前x個字元都相等,這一點next陣列已經幫我們做好了,所以我們只需要比較numnum[i + next[i]]numnum[1 + next[i]]的大小。

int greater =0;

int equal =0;

int less =0;

for(

int i =

1; i <= len; i++

)else

if(numnum[i + next[i]

]< numnum[

1+ next[i]])

else

}printf

("%d %d %d"

, less, equal, greater)

;

下面還剩兩個問題,第乙個,如何去除迴圈,第二個,如何解決前導零。

在解決這兩個問題之前,我們先來看一組樣例。

對於 123123 這個數,我們期望得到的數字是 123123、231231、312312 這 3 個,而不是將 123123 變成 123123123123 之後列舉出來的 6 個。

所以,能不能通過將數字只複製最小的迴圈節來完成?

答案是顯然的。

求迴圈節的過程之前已經介紹過了,就是利用n - next[n]即可,那麼我們只需複製這樣一小節拼在最後就可以了。

// 求出字串 t 的迴圈節長度

intgetloop

(const

char

*t,int length)

if(t[j +1]

== t[i]

)else

}return length - next[length];}

char

*duplicate

(const

char

*s)

那麼與之對應的,for (int i = 1; i <= len; i++)中的len,就應該換成迴圈節的長度getloop(num + 1, strlen(num + 1))

這樣一來,重複數字的問題也就自然而然地被解決了。前導零也不需要特別處理了。

但是這麼做的話,123123可以被正常處理,123123123也可以得到正確的結果,因為對於他們來說,迴圈節都是123,只需要拼接一段迴圈節到最後就可以了。

然而,12312312312求出的迴圈節123可不能被簡單地拼接到最後,因為對於這個數字來說,12312312312才是需要被拼接的。

所以我們需要判斷一下,最後乙個是不是真的完整的迴圈節。

int loop = length - next[length];if

(next[length]

% loop !=0)

else

由此,可以得到完整**:

#include

//#define debug_use_only

#define max_len 100007

void

print

(const

char

*t,const

int*next,

int n)

for(

int i =

1; i <= n; i++)}

// 求出字串 t 的迴圈節長度

計蒜客 旋轉數字 (kmp exkmp)

首先得去重,怎麼去重呢,剛開始想了好幾種方法,都不太理想,後再才想明白,找重複的不就是找找迴圈節嘛,因為如果存在迴圈節,那麼一定會造成重複,比如123123123,迴圈節是3,所以只需要迴圈數字三次即可,再多迴圈就會造成重複,那麼問題就來了,怎麼計算迴圈節呢?在學習kmp中,我們利用fail陣列的功...

計蒜客 單獨的數字

給定乙個陣列 a,除了乙個數出現一次之外,其餘數都出現三次。找出出現一次的數。如 找出 7。你的演算法只能是線性時間的複雜度,並且不能使用額外的空間哦 輸入格式 第一行輸入乙個數n 1 n 500 代表陣列的長度。接下來一行輸入 n 個 int 範圍內 2147483648 2147483647 的...

計蒜客 單獨的數字

給定乙個陣列 a a,除了乙個數出現一次之外,其餘數都出現三次。找出出現一次的數。如 找出 77。你的演算法只能是線性時間的複雜度,並且不能使用額外的空間哦 第一行輸入乙個數 n 1 leq n leq 500 n 1 n 500 代表陣列的長度。接下來一行輸入 n n 個 int 範圍內 2147...