POJ 3865 - Database 字符串hash
【题意】
给一个字符串组成的矩阵,规模为n*m(n<=10000,m<=10),如果某两列中存在两行完全相同,则输出NO和两行行号和两列列号,否则输出YES
【题解】
因为m很小,所以对每一行枚举其中两个字符串,检查之前行中对应的两列里是否重复即可。但是如何判重。
一开始想的把字符串做成pair然后用map映射为行号,但是TLE。
后来想到用hash判重,可能是因为哈希函数不够好,还是TLE。。。
总之这道题卡了三个小时,一直TLE。
枚举每一列,对枚举到的那一列从小到大排序,然后找到相邻两个相等的,接着在相同行,不同列中寻找相等的字符串,如果存在表示找到解。复杂度是m^2*n*logn,处于可以接受的范围。
最后的方法在POJ上使用C++通过,但是G++却超时了。
曾经记得POJ有一道题是使用G++通过,C++超时,总之以后在POJ上超时了就两个编译器都试试。
在看别人的代码时,发现有人用了字符串hash读入:
typedef unsigned long long ULL; ULL hash(char *s) { ULL res = 0; for(int i = 0; s[i]; ++i) { res *= 9837;//质数 res += s[i]; } return res; }
这样做比较速度相当快,程序用时不到1s。
因为没有处理冲突,所以可以用这个方法水过去(笑)
代码:(没有用字符串hash)
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<stack> #include<vector> #include<queue> #include<string> #include<sstream> #define eps 1e-9 #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define FOR(i,j,k) for(int i=j;i<=k;i++) #define MAXN 1005 #define MAXM 40005 #define INF 0x3fffffff using namespace std; typedef long long LL; int i,j,k,n,m,x,T,ans,big,cas,num,len,nodenum,l; bool flag; char s[105],t[105]; int pre; string y[10005][15]; string ex; struct node { string st; int i; } G[10005]; bool cmp(node a,node b) { return a.st<b.st; } int main() { scanf("%d%d",&n,&m); gets(s); for (i=1;i<=n;i++)//读入 { gets(s); len=strlen(s); s[len]=','; s[len+1]='\0'; pre=0; num=0; for (j=0;j<=len;j++) { if (s[j]==',') { s[j]='\0'; y[i][++num]=(s+pre); pre=j+1; } } } for (i=1;i<=m;i++)//枚举列 { for (j=1;j<=n;j++)//把这一列复制一遍后排序 { G[j].st=y[j][i]; G[j].i=j; } sort(G+1,G+1+n,cmp); for (j=1;j<=n;j++)//枚举行 { for (k=j+1; k<=n && G[k].st==G[j].st ;k++)//向下找与第j行相同的行 { for (l=i+1;l<=m;l++)//再枚举其他列,检查是否有重复 { if ( y[ G[j].i ][l]==y[ G[k].i ][l] )//存在重复 { printf("NO\n"); printf("%d %d\n",G[j].i,G[k].i); printf("%d %d\n",i,l); return 0; } } } } } printf("YES\n"); return 0; }