【NOIP模拟赛】lover——心上人
心上人
【问题描述】
人到初三,总会遇到情感问题,比方说小 J 就喜欢上了小 W。于是小 J 就需要说一长串的话讨小 W 欢心。现在已知小 W 听到一些词就会很高兴,而且白听不厌,但她又讨厌小 J 说的话的长度短于或长于她所期望的。所以小 J 只能说一个长度为 L 的字符
串,并使小 W 喜欢的词在其中出现的次数尽量多。小 J 想请你告诉他,一个长度为 L 的字符串中最多出现小 W 喜欢的话多少次。。。
【输入格式】
输入共 2 行。
第 1 行包含 1 个正整数 L,表示 小 J 需要的字符串的长度。
第 2 行包含一个由小写字母组成的字符串 S,表示小 W 喜欢听到的词
【输出格式】
输出共 1 行,一个正整数 Ans,表示小 J 说的话中最多出现多少次小 W 喜欢的词。
(小 W 喜欢的词在小 J 说的话中只要起始位置不同,就算出现一次,即重叠的词计
数)
【输入实例】
5 aba
【输出实例】
2
【其他说明】
对于 30%的数据,保证小 W 喜欢的词不会在文中出现重叠对于 100%的数据,保证|S|<=L<=1000
【试题分析】
如果一看过去觉得自己一分也拿不到的人,那是没救了……
30%的数据其实就是看看输入的字符串like里面有没有一个字符满足:从其开始一直到字符串的最末端是否与另一个与输入字符串相同的串从始至终都是匹配的。
我知道这么说有点绕,但是至少要想明白一点:满足这个字符的点其后面必须跟另一个相同字符串的a[0]往后,知道自己原串这边的尽头坐标全是匹配的。
我们来看看这一块的代码:
lenl=strlen(love); strcpy(love2,love); for(int i=1;i<lenl;i++)//从原串的1到末尾枚举 if(love[i]==love2[0])//如果原串的第i位等于原串的第0位了,那么可以去尝试搜索了 { for(int j=1;j<lenl-i;j++) if(love[i+j]!=love2[j])//对比 {hl=false;break;}//不等于了,就直接继续 }
当然,如果你和厉害的话,可以写烤馍片算法(KMP),由于已经可以了,所以我就不在写了。
然后,我们可以创造一个空字符串去模拟,挨个往里面接。
longl=len; for(int i=0;i<len;i++) slike[i]=str[i],struse[i]=1; for(int i=1;i<L&&longl<=L;i++) { int j=i; if(str[0]==slike[i]) { for(int k=0;k<len;k++) { if(str[k]==slike[i+k]) continue; else if(struse[i+k]==0) { for(;k<len&&longl<=L;k++) { struse[i]==1; slike[i+k]=str[k]; longl++; } if(longl<=L)ans++; break; } } } } cout<<ans;
来看看暴力的代码:
#include<iostream> #include<cstring> using namespace std;char b[1001],c[1001]; int huiwen(char *a) { int n,i,j,k; n=strlen(a); if(n==1) return 0; for(i=0;i<n;i++) b[i]=a[n-i-1]; strcpy(c,a); if(strcmp(c,b)==0) return 1; return 0; } char str[1001],slike[1001]; int struse[1001]; int main() { int L,longl; cin>>L; cin>>str; int len=strlen(str),ans=1; if(huiwen(str)==0) cout<<L/len; else if(L>=len) { longl=len; for(int i=0;i<len;i++) slike[i]=str[i],struse[i]=1; for(int i=1;i<L&&longl<=L;i++) { int j=i; if(str[0]==slike[i]) { for(int k=0;k<len;k++) { if(str[k]==slike[i+k]) continue; else if(struse[i+k]==0) { for(;k<len&&longl<=L;k++) { struse[i]==1; slike[i+k]=str[k]; longl++; } if(longl<=L)ans++; break; } } } } cout<<ans; } else cout<<0; } /* */
当然,这样做是有大BUG的,如果你输入7 abaa,它会输出1,你只需把回文那一块换成上面我们所提到的匹配就好了。
我的最终做法是O(玄学)的,是不是很神奇?只要你维护了求解只需O(1)!
那么怎么做呢?
我的做法都是瞎yy求公式出来的,我们想一想,记录一个beg,表示第一个符合条件点的坐标。
让后把这个坐标以前(包括)的点都从L里面减去,然后按周期求即可。
【代码】
#include<iostream> #include<cstring> using namespace std; char love[1100],love2[1100]; int L,lenl,beg; int ans=0; bool same=false; int main() { scanf("%d%s",&L,&love); lenl=strlen(love); if(lenl>L) {printf("0");return 0;}//如果连一个串都塞不下,那么就可以直接输出 strcpy(love2,love); for(int i=1;i<lenl;i++) if(love[i]==love2[0]) { bool hl=true; for(int j=1;j<lenl-i;j++) if(love[i+j]!=love2[j]) {hl=false;break;}//比对、记录 if(hl==true) { same=true;//标为存在这样的点 beg=i;//记录坐标 break;//我们如果发现一个就可以停止比对了 } } if(same==false)//如果没有符合条件的点,那么就直接输出L/lenl { printf("%d",(int)L/lenl); return 0; } else { if(beg==lenl-1)//推公式,beg在最后一个时跟其它情况不太一样,要单独考虑 { lenl--; L--; int now=beg; ans=(int)(L-lenl)/(lenl); ans++; printf("%d",(int)ans); } else { int L1=lenl-1; L1-=beg; L-=(beg+1); int ans=(int)L/L1; printf("%d",(int)ans); } } }