一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Sample Input
abcde a3
aaaaaa aa
Sample Output
0
3
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; char a[1000100],b[1000100]; int p[1000100]; int main() { while(scanf("%s",a+1)&& a[1] != '#'&&scanf("%s",b+1)){ memset(p,0,sizeof(p));//a为字符串,b为模式串 int la=strlen(a+1),lb=strlen(b+1); int count=0,j=0; p[1]=0; //先处理出p数组,无非是b和自己匹配,与b和a匹配一样,故代码差不多 for(int i=2;i<=lb;i++) { while(j>0 && b[i]!=b[j+1]) j=p[j];//往前翻记录了有相同前缀的j if(b[i]==b[j+1]) j++;//i匹配成功了,i继续往后 p[i]=j; } j=0; //两个字符串匹配 for(int i=1;i<=la;i++) { while(j>0 && a[i]!=b[j+1]) j=p[j]; if(a[i]==b[j+1]) j++; if(j==lb) count++,j=0; } printf("%d\n",count); } return 0; }
典型的KMP板子题(同 HDU 1711 Number Sequence)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; char a[1000100],b[1000100]; int p[1000100]; int main() { int t; scanf("%d",&t); while(t--){ scanf("%s%s",b+1,a+1); memset(p,0,sizeof(p));//a为字符串,b为模式串 int la=strlen(a+1),lb=strlen(b+1); int count=0,j=0; p[1]=0; //先处理出p数组,无非是b和自己匹配,与b和a匹配一样,故代码差不多 for(int i=2;i<=lb;i++) { while(j>0 && b[i]!=b[j+1]) j=p[j];//往前翻记录了有相同前缀的j if(b[i]==b[j+1]) j++;//i匹配成功了,i继续往后 p[i]=j; } j=0; //两个字符串匹配 for(int i=1;i<=la;i++) { while(j>0 && a[i]!=b[j+1]) j=p[j]; if(a[i]==b[j+1]) j++; if(j==lb) count++,j=p[j]; //从下一个模式串 } printf("%d\n",count); } return 0; }
方法一:用string和map解决
#include<stdio.h> #include<iostream> #include<cstring> #include<algorithm> #include<map> #include<string.h> using namespace std; map<string,int>pre; int main(){ char a[12]; while(gets(a)){ if(a[0]=='\0')//或者写成==NULL break; string str=a;//用字符串来存储字符数组a for(int i=1;i<=str.size();i++){ string s=str.substr(0,i);//在string中使用 pre[s]++; } /* string s=""; for(int i=0;a[i];i++) { s+=a[i];//这样写也可以 pre[s]++; } */ } while(gets(a)) { string s=a; printf("%d\n",pre[s]); } return 0; }
方法二:采用二维数组用字典树来解决
#include <iostream> #include <stdio.h> using namespace std; int trie[1000010][26]; //数组形式定义字典树,值存储的是下一个字符的位置 int num[1000010]={0}; //附加值,以某一字符串为前缀的单词的数量 int pos = 1; void Insert(char word[]) //在字典树中插入某个单词 { int i; int c = 0; for(i=0;word[i];i++){ int n = word[i]-'a'; if(trie[c][n]==0) //如果对应字符还没有值 trie[c][n] = pos++; c = trie[c][n]; num[c]++; } } int Find(char word[]) //返回以某个字符串为前缀的单词的数量 { int i; int c = 0; for(i=0;word[i];i++){ int n = word[i]-'a'; if(trie[c][n]==0) return 0; c = trie[c][n]; } return num[c]; } int main() { char word[11]; while(gets(word)){ if(word[0]==NULL) //空行。gets读入的回车符会自动转换为NULL。 break; Insert(word); } while(gets(word)) printf("%d\n",Find(word)); return 0; } /* HDU1251 与前缀统计差不多,注意格式 banana band bee absolute acm ba b band abc */
本题主要考察的是字符串的输入,题目是要通过词义检索单词。
方法一:用map<string,string>进行查找
#include <iostream> #include <string> #include <map> #include<stdio.h> using namespace std; map <string,string>mp; int main () { char ch[30],str1[15],str2[15]; while (gets(ch)) { if (ch[0]==NULL) break; sscanf(ch,"%s%s",str1,str2); mp[str2]=str1; } while (gets(ch)) { map<string,string>::iterator it; it=mp.find(ch);//查找 if (it!=mp.end()) cout<<(*it).second<<endl; else printf("eh\n"); } return 0; }
方法二:通过对字符串排序二分查找答案
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; struct node { char s1[20],s2[20]; } a[100005]; int cmp(node a,node b) { return strcmp(a.s2,b.s2)<0; // 若字符串1>字符串2 则返回1, //字符串1<字符串2 则返回 -1,相等返回0。 } int main() { int i,j,low,mid,high,flag=1,len=0; char s[50]; while(gets(s)) { //当是空行时,字符串的长度是零;或者字符串第一位是'\0' //if(s[0]=='\0') if(strlen(s)==0) break; //sscanf可以从s中读取两个字符串 sscanf(s,"%s%s",a[len].s1,a[len].s2); len++; } sort(a,a+len,cmp);//按字典序排列所以字符串 //二分查找字符串 while(gets(s)) { low=0; high=len-1; flag=1; while(low<=high) { int mid =(low+high)/2; if(strcmp(s,a[mid].s2)==0) { printf("%s\n",a[mid].s1); flag = 0; break; } else if(strcmp(s,a[mid].s2)>0) low=mid+1; else high=mid-1; } if(flag==1) printf("eh\n"); } return 0; }
题目链接: link.
题意:已知A串,B串,和整数k,求B的最长前缀,此前缀在A串中出现的次数大于等于k次
这里很容易想到二分,但一个一个去枚举判断答案(如下)会超出时间
bool check(char s[],char t[],int mid,int k){ str2=s; str1=""; for(int i=0;i<mid;i++) str1=str1+t[i]; for(int i=0;i<str2.size()-mid+1;i++){ str=str2.substr(i,mid); if(str==str1) mp[str1]++; if(mp[str1]>=k){ mp.clear(); return 1; } } mp.clear(); return 0; }
因此我们联想到KMP算法,代码如下:
#include<stdio.h> #include<algorithm> #include<map> #include<cstring> using namespace std; string str,str1,str2; char s[100005]; char t[100005]; int k,l,r,mid; map<string,int>mp;//map映射 bool check(int mid,int k){ int p[100005]={0}; memset(p,0,sizeof(p));//a为字符串,b为模式串 int la=strlen(s+1),lb=mid; int count=0,j=0; p[1]=0; //先处理出p数组,无非是b和自己匹配,与b和a匹配一样,故代码差不多 for(int i=2;i<=lb;i++) { while(j>0 && t[i]!=t[j+1]) j=p[j];//往前翻记录了有相同前缀的j if(t[i]==t[j+1]) j++;//i匹配成功了,i继续往后 p[i]=j; } j=0; //两个字符串匹配 for(int i=1;i<=la;i++) { while(j>0 && s[i]!=t[j+1]) j=p[j]; if(s[i]==t[j+1]) j++; if(j==lb) count++,j=p[j]; //从下一个模式串 } // printf("la %d lb %d\n",la,lb); // printf("%d\n",count); if(count>=k) return 1; else return 0; } int main(){ gets(s+1); gets(t+1); scanf("%d",&k); l=0,r=strlen(t+1); bool flag=0; while(l<r){ mid=(l+r+1)/2; if(check(mid,k)){ flag=1; l=mid; } else r=mid-1; } if(flag==1){ for(int i=1;i<=l;i++) printf("%c",t[i]); } else printf("IMPOSSIBLE\n"); return 0; }
题意:求字符串末尾最少添加多少个字符可以把他变成回文串,输出这个回文串,很明显只要用KMP[判断一下反串和字符串最多能匹配到哪个位置,结尾加上反串这个位置之后的所有字符就可以了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; char a[1000100],b[1000100]; int p[1000100]; int main() { while(gets(a+1)){ for(int i=1;i<=strlen(a+1);i++) b[strlen(a+1)-i+1]=a[i]; memset(p,0,sizeof(p));//a为字符串,b为模式串 int la=strlen(a+1),lb=la; int count=0,j=0; p[1]=0; //先处理出p数组,无非是b和自己匹配,与b和a匹配一样,故代码差不多 for(int i=2;i<=lb;i++) { while(j>0 && b[i]!=b[j+1]) j=p[j];//往前翻记录了有相同前缀的j if(b[i]==b[j+1]) j++;//i匹配成功了,i继续往后 p[i]=j; } j=0; //两个字符串匹配 for(int i=1;i<=la;i++) { while(j>0 && a[i]!=b[j+1]) j=p[j]; if(a[i]==b[j+1]) j++;//j为最多可以匹配到的位置 // if(j==lb) // count++,j=p[j]; //从下一个模式串 } for(int i=1;i<=la;i++) printf("%c",a[i]); for(int i=j+1;i<=la;i++) printf("%c",b[i]); printf("\n"); } return 0; }