1、HDU3374 String Problem KMP+最小表示法 **
大致题意:求出给定字符串的最小表示和最大表示以及出现次数
思路:环状字符串某一链状字符串出现的次数为循环节个数,用Next数组求解。然后是字符串的最大表示法和最小表示法。
AC代码:
1 #include<iostream> 2 #include<sstream> 3 #include<fstream> 4 #include<algorithm> 5 #include<cstring> 6 #include<iomanip> 7 #include<cstdlib> 8 #include<cctype> 9 #include<vector> 10 #include<string> 11 #include<cmath> 12 #include<ctime> 13 #include<stack> 14 #include<queue> 15 #include<map> 16 #include<set> 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define random(a,b) (rand()%(b-a+1)+a) 19 #define ll long long 20 #define ull unsigned long long 21 #define e 2.71828182 22 #define Pi acos(-1.0) 23 #define ls(rt) (rt<<1) 24 #define rs(rt) (rt<<1|1) 25 #define lowbit(x) (x&(-x)) 26 using namespace std; 27 const int MAXN=1e6+5; 28 string str; 29 int Next[MAXN],len; 30 int getmin() 31 { 32 int i=0,j=1,k=0; 33 while(i<len&&j<len&&k<len) 34 { 35 if(str[(i+k)%len]==str[(j+k)%len]) k++; 36 else 37 { 38 if(str[(i+k)%len]>str[(j+k)%len]) i+=k+1; 39 else j+=k+1; 40 if(i==j) j++; 41 k=0; 42 } 43 } 44 return min(i,j)+1; 45 } 46 int getmax() 47 { 48 int i=0,j=1,k=0; 49 while(i<len&&j<len&&k<len) 50 { 51 if(str[(i+k)%len]==str[(j+k)%len]) k++; 52 else 53 { 54 if(str[(i+k)%len]>str[(j+k)%len]) j+=k+1; 55 else i+=k+1; 56 if(i==j) j++; 57 k=0; 58 } 59 } 60 return min(i,j)+1; 61 } 62 void GetNext() 63 { 64 int k=Next[0]=-1; 65 int j=0; 66 while(j<len) 67 { 68 if(k==-1||str[j]==str[k]) Next[++j]=++k; 69 else k=Next[k]; 70 } 71 } 72 int read() 73 { 74 int s=1,x=0; 75 char ch=getchar(); 76 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 77 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 78 return x*s; 79 } 80 81 int main() 82 { 83 ios::sync_with_stdio(false); 84 cin.tie(0);cout.tie(0); 85 while(cin>>str) 86 { 87 len=str.size(); 88 GetNext(); 89 int t=1; 90 if(Next[len]!=0&&len%(len-Next[len])==0) t=len/(len-Next[len]); 91 cout<<getmin()<<' '<<t<<' '<<getmax()<<' '<<t<<endl; 92 } 93 }
2、POJ3349 Snowflake Snow Snowflakes Hash+最小表示法 **
大致题意:每一片雪花都有各自的6个arm值,如果两片雪花从相同或者不同位置开始顺时针数或者逆时针数可以匹配上,那么这两片雪花就是相等的。给定n片雪花,判断是否存在有相同的两片雪花。
思路:利用Hash匹配是否有相等的两片雪花;利用最小表示法表示每片雪花,那么每片雪花就只有两个Hash值需要判断,正序或逆序。
AC代码:
1 #include<iostream> 2 #include<sstream> 3 #include<fstream> 4 #include<algorithm> 5 #include<cstring> 6 #include<iomanip> 7 #include<cstdlib> 8 #include<cctype> 9 #include<vector> 10 #include<string> 11 #include<cmath> 12 #include<ctime> 13 #include<stack> 14 #include<queue> 15 #include<map> 16 #include<set> 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define random(a,b) (rand()%(b-a+1)+a) 19 #define ll long long 20 #define ull unsigned long long 21 #define e 2.71828182 22 #define Pi acos(-1.0) 23 #define ls(rt) (rt<<1) 24 #define rs(rt) (rt<<1|1) 25 #define lowbit(x) (x&(-x)) 26 using namespace std; 27 const int Base=23333; 28 const int MOD=1e3+7; 29 int a[6]; 30 int n; 31 vector<ull> HashTable[MOD]; 32 void add_edge(ull ha) 33 { 34 int pos=ha%MOD; 35 HashTable[pos].push_back(ha); 36 } 37 bool find(ull ha) 38 { 39 int pos=ha%MOD; 40 for(int i=0;i<HashTable[pos].size();++i) 41 if(HashTable[pos][i]==ha) return true; 42 return false; 43 } 44 int getmin() 45 { 46 int i=0,j=1,k=0; 47 while(i<6&&j<6&&k<6) 48 { 49 if(a[(i+k)%6]==a[(j+k)%6]) k++; 50 else 51 { 52 if(a[(i+k)%6]>a[(j+k)%6]) i+=k+1; 53 else j+=k+1; 54 if(i==j) j++; 55 k=0; 56 } 57 } 58 return min(i,j); 59 } 60 int read() 61 { 62 int s=1,x=0; 63 char ch=getchar(); 64 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 65 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 66 return x*s; 67 } 68 int main() 69 { 70 n=read(); 71 ull ha; 72 bool flag=true; 73 while(n--) 74 { 75 for(int i=0;i<6;++i) 76 a[i]=read(); 77 if(flag) 78 { 79 int idx=getmin(); 80 ha=0; 81 for(int i=idx;i<idx+6;++i)//正序 82 ha=ha*Base+a[i%6]; 83 if(find(ha)) flag=false; 84 add_edge(ha); 85 ha=0; 86 for(int i=idx;i>idx-6;--i)//逆序 87 ha=ha*Base+a[(i+6)%6]; 88 if(find(ha)) flag=false; 89 add_edge(ha); 90 } 91 } 92 if(flag) cout<<"No two snowflakes are alike. "; 93 else cout<<"Twin snowflakes found."; 94 }
3、51Nod1277 字符串中的最大值 KMP+DP **
大致题意:求前缀的出现次数与前缀长度之积的最大值
思路:用dp[i]表示前缀S[1..i]出现次数,初始化所有dp[i]=1,逆序推,然后思考Next数组的意义
以abababa为例:
i=7,前缀abababa一定出现了一次(本身),因为Next[7]=5,因此前缀S[1..7]中前缀S[1..5]和后缀S[3..7]相等,即S[1..5]在这出现了一次,因此dp[5]+=dp[7]
i=6,前缀abababa一定出现了一次(本身),因为Next[6]=0,因此前缀S[1..6]中任意一个前缀都没得匹配的。
....
Next[i]表示的是最长公共前缀后缀,其实,不为0的时候不就说明某个前缀S1的前缀S2与前缀S1的后缀S3相等也就是出现过吗。
1 #include<iostream> 2 #include<sstream> 3 #include<fstream> 4 #include<algorithm> 5 #include<cstring> 6 #include<iomanip> 7 #include<cstdlib> 8 #include<cctype> 9 #include<vector> 10 #include<string> 11 #include<cmath> 12 #include<ctime> 13 #include<stack> 14 #include<queue> 15 #include<map> 16 #include<set> 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define random(a,b) (rand()%(b-a+1)+a) 19 #define ll long long 20 #define ull unsigned long long 21 #define e 2.71828182 22 #define Pi acos(-1.0) 23 #define ls(rt) (rt<<1) 24 #define rs(rt) (rt<<1|1) 25 #define lowbit(x) (x&(-x)) 26 using namespace std; 27 const int MAXN=1e5+5; 28 string str; 29 ll N,Next[MAXN],len; 30 ll dp[MAXN]; 31 int read() 32 { 33 int s=1,x=0; 34 char ch=getchar(); 35 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 36 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 37 return x*s; 38 } 39 void GetNext() 40 { 41 int k=Next[0]=-1; 42 int j=0; 43 while(j<len) 44 { 45 if(k==-1||str[j]==str[k]) Next[++j]=++k; 46 else k=Next[k]; 47 } 48 } 49 int main() 50 { 51 cin>>str; 52 len=str.size(); 53 GetNext(); 54 for(int i=len;i>=1;--i) 55 { 56 /*ll tmp=Next[i]; 57 dp[i]++; 58 while(tmp) 59 { 60 dp[tmp]++; 61 tmp=Next[tmp]; 62 }*/ 63 dp[i]++; 64 dp[Next[i]]+=dp[i]; 65 } 66 /*for(int i=1;i<=len;++i) 67 cout<<dp[i]<<' ';cout<<endl;*/ 68 ll ans=-1; 69 for(int i=1;i<=len;++i) 70 ans=max(ans,dp[i]*i); 71 cout<<ans<<endl; 72 }