標籤:最大值 spl 長度範圍 seq isp sequence rac scanf scan
本來想做數論的……但是別的dalao都在做制胡竄
所以……
Chapter I KMP
KMP 最關鍵的不是這個半暴力的單模匹配
而是這個nxt數組 經常出一些奇怪的題 尤其是迴圈節可以直接由T-nxt[T]得到……神啊
總之記住nxt就是最長公用前尾碼中首碼的尾指標就OK
T1 poj3461 Oulipo
Time cost: 10min
純純的板子 真沒啥可說的
就是每次清空nxt就OK
Code:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int N = 1000005; 5 #define rep(i,a,n) for(int i = a;i <= n;i++) 6 #define ms(a,b) memset(a,b,sizeof a) 7 8 char s[N],t[N]; 9 int S,T;10 int nxt[N];11 int ans;12 void clr(){ms(nxt,0),ans = 0;}13 void gnxt() {14 int tmp = 0;15 rep(i,2,T) {16 while(tmp && t[tmp+1] != t[i]) tmp = nxt[tmp];17 if(t[tmp+1] == t[i]) nxt[i] = ++tmp;18 }19 }20 void KMP() {21 int tmp = 0;22 rep(i,1,S) {23 while(tmp && t[tmp+1] != s[i]) tmp = nxt[tmp];24 if(t[tmp+1] == s[i]) {25 tmp++;26 if(tmp == T) ans++;27 }28 }29 }30 31 32 int main() {33 int q;34 scanf("%d",&q);35 while(q--) {36 clr();37 scanf("%s",t+1),scanf("%s",s+1);38 S = strlen(s+1),T = strlen(t+1);39 gnxt();40 KMP();41 printf("%d\n",ans);42 }43 }View Code
T2 poj 2406 Power strings
Time cost:55min
之前曾經遇到過……最後暴力滾粗
今天算是靠著打表理解了
由於nxt定義的時候刨去了串本身是自己的最長公用前尾碼
在最後一次求nxt的時候 我們考慮被nxt[T]拋棄的部分(記為len)
如果這一段能整除T,那麼考慮第二段len長的串
它與第一段len長的串是相等的
同理可以推到T/len倍
同時 nxt取最大值 咕T-nxt[T]取最小
所以如果T是T-nxt[T]的整數倍 那麼T-nxt[T]就是最小迴圈節
Code:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 const int N = 1000005; 5 #define rep(i,a,n) for(int i = a;i <= n;i++) 6 #define ms(a,b) memset(a,b,sizeof a) 7 8 char s[N],t[N]; 9 int S,T;10 int nxt[N];11 int ans;12 void clr(){ms(nxt,0),ans = 0;}13 void gnxt() {14 int tmp = 0;15 rep(i,2,T) {16 while(tmp && t[tmp+1] != t[i]) tmp = nxt[tmp];17 if(t[tmp+1] == t[i]) nxt[i] = ++tmp;18 }19 }20 void KMP() {21 int tmp = 0;22 rep(i,1,S) {23 while(tmp && t[tmp+1] != s[i]) tmp = nxt[tmp];24 if(t[tmp+1] == s[i]) {25 tmp++;26 if(tmp == T) ans++;27 }28 }29 }30 31 32 int main() {33 while(1) {34 clr();35 scanf("%s",t+1);36 T = strlen(t+1);37 if(T == 1 && t[1] == ‘.‘) return 0;38 gnxt();39 if(T % (T - nxt[T]) == 0) printf("%d\n",T/(T-nxt[T]));40 else puts("1");41 }42 }View Code
T3 CF562D Om Nom and Necklace
Time cost:45min
有了前一道題做鋪墊 還是比較容易想到的
交叉沒法做 我們視AB為迴圈節
由於k給定 所以通過i和k可以求出一個迴圈節長度範圍
如果這個長度是由nxt求出的最短迴圈節的整數倍就OK
實現的時候是 除len然後判斷合法區間是否存在 這樣就可以自動減掉後面剩的一段A
但是要注意 A或B為空白的情況就是分成k段或者k+1段純迴圈節(沒有尾碼) 可以特判
(實際A為空白已經處理過了)
Code:
#include<cstdio>#include<cstring>using namespace std;const int N = 1000005;#define rep(i,a,n) for(int i = a;i <= n;i++)#define ms(a,b) memset(a,b,sizeof a)char s[N],t[N];int S,T;int nxt[N];int ans;void clr(){ms(nxt,0),ans = 0;}void gnxt() { int tmp = 0; rep(i,2,T) { while(tmp && t[tmp+1] != t[i]) tmp = nxt[tmp]; if(t[tmp+1] == t[i]) nxt[i] = ++tmp; }}void KMP() { int tmp = 0; rep(i,1,S) { while(tmp && t[tmp+1] != s[i]) tmp = nxt[tmp]; if(t[tmp+1] == s[i]) { tmp++; if(tmp == T) ans++; } }}int n,k;int main() { scanf("%d%d",&n,&k); scanf("%s",t+1); T = n; gnxt(); rep(i,1,T) { int len = i - nxt[i]; if(i % (k+1) == 0 && (i / (k+1)) % len == 0) putchar(‘1‘);//lenb==0 else { //circular subsequence range int upp = i / k,down = i / (k + 1) + 1; upp = upp / len; down = (down + len - 1) / len; if(upp >= down) putchar(‘1‘); else putchar(‘0‘); } } puts(""); return 0;}View Code
To be continued...
Practice II 字串