計蒜客 - 旋轉數字
蒜頭君發現了乙個很好玩的事情,他對乙個數作旋轉操作,把該數的最後的數字移動到最前面。比如,數 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 個了。
所以我們只需要比較大於,或者小於原數的數字。
給定兩個字串s1
和s2
,如果已知他們開始的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...