【HDU5510 2015沈阳赛区B】【KMP or strstr for循环剪枝】Bazinga 循环处思维灵活转化 时间复杂度均摊思想

来源:互联网 发布:淘宝确认收货无法评价 编辑:程序博客网 时间:2024/05/16 20:27


Bazinga

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 235    Accepted Submission(s): 98


Problem Description
Ladies and gentlemen, please sit up straight.
Don't tilt your head. I'm serious.

For n given strings S1,S2,,Sn, labelled from 1 to n, you should find the largest i (1in) such that there exists an integer j (1j<i) and Sj is not a substring of Si.

A substring of a string Si is another string that occurs in Si. For example, ``ruiz" is a substring of ``ruizhang", and ``rzhang" is not a substring of ``ruizhang".
 

Input
The first line contains an integer t (1t50) which is the number of test cases.
For each test case, the first line is the positive integer n (1n500) and in the following n lines list are the strings S1,S2,,Sn.
All strings are given in lower-case letters and strings are no longer than 2000 letters.
 

Output
For each test case, output the largest label you get. If it does not exist, output 1.
 

Sample Input
45ababczabcabcdzabcd4youlovinyouaboutlovinyouallaboutlovinyou5dedefabcdabcdeabcdef3abaccc
 

Sample Output
Case #1: 4Case #2: -1Case #3: 4Case #4: 3
 

Source
2015ACM/ICPC亚洲区沈阳站-重现赛(感谢东北大学)
 

 
strstr写法:
#include<stdio.h>#include<string.h>#include<ctype.h>#include<math.h>#include<iostream>#include<string>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}#define MS(x,y) memset(x,y,sizeof(x))#define MC(x,y) memcpy(x,y,sizeof(x))#define MP(x,y) make_pair(x,y)#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}const int N=0,M=0,Z=1e9+7,ms63=1061109567;int casenum,casei;int n;char s[505][2020];bool e[505];int bf(){for(int i=n;i>=1;i--){for(int j=1;j<i;j++)if(!strstr(s[i],s[j]))return i;}return -1;}int solve(){MS(e,1);int ans=-1;for(int i=1;i<n;i++)//枚举子串{for(int j=i+1;j<=n;j++)if(e[j])//枚举还不一定是满足串的母串{//如果j是i之后的第一个满足要求的串,使得s[i]是s[j]的子串,那么s[i]对于后串的意义s[j]都能起到。使s[i] break即可。if(strstr(s[j],s[i]))break;//否则s[i]不是s[j]的子串,那么s[j]就是满足要求的串,以后就不用再比较了。else {e[j]=0;gmax(ans,j);}}}return ans;}int main(){scanf("%d",&casenum);for(casei=1;casei<=casenum;casei++){scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%s",s[i]);//printf("Case #%d: %d\n",casei,bf());printf("Case #%d: %d\n",casei,solve());}return 0;}/*【题意】有共计T([1,50])组数据给你n([1,500])个串,每个串都是小写字符串,长度可达2000。定义:s[i]是满足要求的串的条件是——至少存在一个j(1<=j<i),s[j]不是s[i]的子串。让你找到最大的i(1<=i<=n),使得s[i]是满足要求的串。【类型】for循环处思维灵活转化 时间复杂度均摊思想 【分析】暴力做法有两种。首先,最直接最暴力的做法是——int bf(){for(int i=n;i>=1;i--){for(int j=1;j<i;j++){if(!strstr(s[j],s[i]))return i;}}return -1;}这个时间复杂度是O(Tnnlen),可达50*500*500*2000=250e8,即250亿,爆炸。但是转念一想,这种数据所对应的输出可达50*500*2000=5e7,即50MB,是不可能的,读入就爆炸了。于是,如果常数小,这样做是可以AC的。而事实上,对于随机数据,这种暴力其实比下面的暴力,效率更高,更容易AC。然后,另外一种暴力,是我想要针对特定构造数据(其实是针对了自己TwT)所写。以每个串为子串筛后面的所有串:如果它不是后串的子串,那么break掉。答案不会比当前后串的编号小。如果它是后串的子串,那么我们可以把后串删掉。(就是这里想错了。我们应当删掉的串不是后串,而是这个串——如果顺着这里想下去,改变for循环的顺序,for j=i+1 to n,也许就能很快做出来了。唉,还是自己思考的时间太少了,思维太不灵活了。)正确的做法是什么呢?参照代码,先升序枚举i,再升序枚举j。如果j是i之后的第一个满足要求的串,使得s[i]是s[j]的子串,那么s[i]对于后串的意义s[j]都能起到。使s[i] break即可。否则s[i]不是s[j]的子串,那么s[j]就是满足要求的串,以后就不用再比较"某个串是不是s[j]的子串"了。这个时间复杂度是什么呢?对于每次(s[i]是不是s[j])的子串:如果是,break掉,s[i]不再匹配;如果不是,s[j]被确定满足要求,s[j]不再匹配。所以每次比较都会有一个串失去后序匹配作用。于是时间复杂度是O(T(n^2+nlen)),最大不过50*500*2000=5e7,就是一个完全可以AC的时间复杂度了。【时间复杂度&&优化】O(Tnnlen)->O(T(n^2+nlen))【trick&&吐槽】1,题目:做题要看题目名称暗示。B题题目bazinga是"逗你玩"的意思,然后我们真的被这题捉弄了。2,读题:不要太依赖队友的读题,做一道题之前一定要自己读一遍,形成一个独立、系统的认知。很多时候,水题做不出来,都是队友开题,然后甩给我,我的思维附带了他们之前的错误思路,也就很难走出去。3,策略:不要让队友卡题,尤其这种傻X题,不如让自己来卡。不要对自己生疏的算法有所恐惧,要挑起队伍的旗帜。4,思维:思维要灵活。这题其实关键就是两个for循环的顺序,只要我都试着思考下,尝试下,很快就能做出来的。5,我一开始的暴力做法,是想要剪枝的,但是在细节地方没有想清楚,可是如果抓住问题,思维严谨有序地想下去,也会很快出解。思考时间(而不是编码时间)应该是解决问题的大头,想清楚细节再做题是非常重要的。6,strstr(母串,子串)返回的是NULL或者母串的匹配首位点指针。这个实际比KMP都要快。用这个的话这道题也不会卡住了。总结——所以对于水题:1,我来做2,重新系统读遍题3,灵活地做思维转化【数据】input45ababczabcabcdzabcd4youlovinyouaboutlovinyouallaboutlovinyou5dedefabcdabcdeabcdef3abacccoutputCase #1: 4Case #2: -1Case #3: 4Case #4: 3*/

KMP写法

#include<stdio.h>#include<string.h>#include<ctype.h>#include<math.h>#include<iostream>#include<string>#include<set>#include<map>#include<vector>#include<queue>#include<bitset>#include<algorithm>#include<time.h>using namespace std;void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);}#define MS(x,y) memset(x,y,sizeof(x))#define MC(x,y) memcpy(x,y,sizeof(x))#define MP(x,y) make_pair(x,y)#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}const int N=0,M=0,Z=1e9+7,ms63=1061109567;int casenum,casei;int n;char s[505][2020];bool e[505];int len[505];int nxt[505][2020];//求得模板串的fail指针void getnxt(int u){int j=-1;nxt[u][0]=-1;for(int i=1;i<len[u];i++){while(j>=0&&s[u][j+1]!=s[u][i])j=nxt[u][j];if(s[u][j+1]==s[u][i])j++;nxt[u][i]=j;}}//查询匹配串s[v]中是否含有匹配串s[u]bool kmp(int v,int u){int j=-1;for(int i=0;i<len[v];i++){while(j>=0&&s[u][j+1]!=s[v][i])j=nxt[u][j];if(s[u][j+1]==s[v][i])j++;if(j==len[u]-1)return 1;}return 0;}int solve(){MS(e,1);int ans=-1;for(int i=1;i<n;i++)//枚举子串{for(int j=i+1;j<=n;j++)if(e[j])//枚举还不一定是满足串的母串{//如果j是i之后的第一个满足要求的串,使得s[i]是s[j]的子串,那么s[i]对于后串的意义s[j]都能起到。使s[i] break即可。if(kmp(j,i))break;//否则s[i]不是s[j]的子串,那么s[j]就是满足要求的串,以后就不用再比较了。else {e[j]=0;gmax(ans,j);}}}return ans;}int main(){scanf("%d",&casenum);for(casei=1;casei<=casenum;casei++){scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",s[i]);len[i]=strlen(s[i]);getnxt(i);}printf("Case #%d: %d\n",casei,solve());}return 0;}/*【题意】有共计T([1,50])组数据给你n([1,500])个串,每个串都是小写字符串,长度可达2000。定义:s[i]是满足要求的串的条件是——至少存在一个j(1<=j<i),s[j]不是s[i]的子串。让你找到最大的i(1<=i<=n),使得s[i]是满足要求的串。【类型】for循环处思维灵活转化 时间复杂度均摊思想 【分析】暴力做法有两种。首先,最直接最暴力的做法是——int bf(){for(int i=n;i>=1;i--){for(int j=1;j<i;j++){if(!strstr(s[j],s[i]))return i;}}return -1;}这个时间复杂度是O(Tnnlen),可达50*500*500*2000=250e8,即250亿,爆炸。但是转念一想,这种数据所对应的输出可达50*500*2000=5e7,即50MB,是不可能的,读入就爆炸了。于是,如果常数小,这样做是可以AC的。而事实上,对于随机数据,这种暴力其实比下面的暴力,效率更高,更容易AC。然后,另外一种暴力,是我想要针对特定构造数据(其实是针对了自己TwT)所写。以每个串为子串筛后面的所有串:如果它不是后串的子串,那么break掉。答案不会比当前后串的编号小。如果它是后串的子串,那么我们可以把后串删掉。(就是这里想错了。我们应当删掉的串不是后串,而是这个串——如果顺着这里想下去,改变for循环的顺序,for j=i+1 to n,也许就能很快做出来了。唉,还是自己思考的时间太少了,思维太不灵活了。)正确的做法是什么呢?参照代码,先升序枚举i,再升序枚举j。如果j是i之后的第一个满足要求的串,使得s[i]是s[j]的子串,那么s[i]对于后串的意义s[j]都能起到。使s[i] break即可。否则s[i]不是s[j]的子串,那么s[j]就是满足要求的串,以后就不用再比较"某个串是不是s[j]的子串"了。这个时间复杂度是什么呢?对于每次(s[i]是不是s[j])的子串:如果是,break掉,s[i]不再匹配;如果不是,s[j]被确定满足要求,s[j]不再匹配。所以每次比较都会有一个串失去后序匹配作用。于是时间复杂度是O(T(n^2+nlen)),最大不过50*500*2000=5e7,就是一个完全可以AC的时间复杂度了。【时间复杂度&&优化】O(Tnnlen)->O(T(n^2+nlen))【trick&&吐槽】1,题目:做题要看题目名称暗示。B题题目bazinga是"逗你玩"的意思,然后我们真的被这题捉弄了。2,读题:不要太依赖队友的读题,做一道题之前一定要自己读一遍,形成一个独立、系统的认知。很多时候,水题做不出来,都是队友开题,然后甩给我,我的思维附带了他们之前的错误思路,也就很难走出去。3,策略:不要让队友卡题,尤其这种傻X题,不如让自己来卡。不要对自己生疏的算法有所恐惧,要挑起队伍的旗帜。4,思维:思维要灵活。这题其实关键就是两个for循环的顺序,只要我都试着思考下,尝试下,很快就能做出来的。5,我一开始的暴力做法,是想要剪枝的,但是在细节地方没有想清楚,可是如果抓住问题,思维严谨有序地想下去,也会很快出解。思考时间(而不是编码时间)应该是解决问题的大头,想清楚细节再做题是非常重要的。6,strstr(母串,子串)返回的是NULL或者母串的匹配首位点指针。这个实际比KMP都要快。用这个的话这道题也不会卡住了。总结——所以对于水题:1,我来做2,重新系统读遍题3,灵活地做思维转化【数据】input45ababczabcabcdzabcd4youlovinyouaboutlovinyouallaboutlovinyou5dedefabcdabcdeabcdef3abacccoutputCase #1: 4Case #2: -1Case #3: 4Case #4: 3*/


1 0
原创粉丝点击