Practice II 字符串
Posted yuyanjiab
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Practice II 字符串相关的知识,希望对你有一定的参考价值。
本来想做数论的……但是别的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 ",ans); 42 } 43 }
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 ",T/(T-nxt[T])); 40 else puts("1"); 41 } 42 }
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; }
To be continued...
以上是关于Practice II 字符串的主要内容,如果未能解决你的问题,请参考以下文章
1.1.2A+B for Input-Output Practice (II)
梯度下降实用技巧II之学习率 Gradient descent in practice II -- learning rate
(HDU)1090 --A+B for Input-Output Practice (II)(输入输出练习(II))