hash、hashtable
这题是真好,问我一共wa+tle共20+才做出来
正经做法好像是扩展kmp+字典树
但是,我想直接用hash做
于是我使用了hash
(我觉得hash是真的nb,太强了!适用范围太广了!必须要好好掌握的!)
首先输入就十分的困难
我们知道所有的字符串的长度之和的上限
但是因为字符串可能非常之多,所以我们难以求解分配给每一个字符串空间
在这里我学到了一个处理方法。
我们开一个上限空间的数组
然后每个字符串都搞一个头指针
这个指针指向上限数组中他们开始的位置。
真的非常的巧妙。very good
然后是下一个难关:
我们该如何hash呢?
我们想想哈,我们hash后肯定要统计个数。
那么这个统计个数,因为hash很大,我们不能依靠统计数组,只能依靠map
ok那么我们接下如何hash呢?
目前还美誉能卡掉双hash的问题
所以我上来就是双hash哦(推荐使用1e9+7和1e9+9 孪生素数很强的)
很快啊,然后tle
看样子双hash是会超时的。
然后我就使用了单hash了,我使用的是1e9+7不行!有冲突!!!!!!
太痛苦了。
然后我是用自然溢出ac了。。。。。。。
小知识:hash时理论上取模的数越大hash的冲突率就越小哦(当然不排除出题人故意卡你的情况)
如此似乎我们就完成了这道题了。
但是我再一看,我的时间5s+
这真的是太慢了!!!!!
怎么办呢?
我们分析了一下,不难发现在map这一步时我们加了一个log的复杂度
很伤,如果出题人再变态一点是会来卡人的。
那么如何解决这个问题?替代map!!!!!!!
我称呼其为hashtable
其实是不难的技巧
我们可以再算出hash值后,假设为v
然后将v对一个较小的素数取模,u=v%mod
然后将u记录在统计数组中。统计数组只要开到mod打就可以了。
然后我们将会面临hash冲突的风险。
如何规避这种风险呢?
我们可以在统计数组中每一索引处再开一个链表就可以了。
用类似链式向前星的形式,存储真实的hash值就好了。
建议,存储进去的值最好不要重复。我们可以再开一个数组记录这个点的值出现多少次了。
为什么这样做呢?因为这回减少链式向前星的长度,在一定程度上会起到制约复杂度飙升的作用。
如此改良后速度2s+
非常出色的hash运用。
#include<cstdio> #include<map> #include<algorithm> using namespace std; typedef unsigned long long ll; const int max_n = 2e6+100; const int mod = 4000037; const ll base = 23; ll p[max_n],tot[max_n*3],*cur = tot; char ss[max_n]; struct My_Hash{ ll *ha; void init(int len){ ha = cur;ha[0]=0; for (int i=1;i<=len;++i)ha[i]=ha[i-1]*base+ss[i-1]; cur+=len+2; } ll gethash(int l, int r){ return ha[r]-ha[l-1]*p[r-l+1]; } }S[max_n],S2[max_n]; struct hashtable{ int head[mod+5],net[max_n],cnt; ll to[max_n],cct[max_n]; void init(){ fill(to,to+max_n,-1);cnt=1; } void insert(ll v){ int u = v%mod; for (int i=head[u];i;i=net[i]){ if (to[i]==v){ ++cct[i]; return; } } to[cnt]=v;cct[cnt]=1; net[cnt]=head[u];head[u]=cnt++; } ll quiry(ll v){ int u = v%mod; for (int i=head[u];i;i=net[i]) if (to[i]==v)return cct[i]; return 0; } }HT; int len[max_n]; int main(){ int n;scanf("%d",&n);p[0]=1; HT.init(); for (int i=1;i<max_n;++i)p[i]=p[i-1]*base; for (int i=1;i<=n;++i){ scanf("%d %s",&len[i],ss); S[i].init(len[i]); HT.insert(S[i].gethash(1,len[i])); for (int j=0;j<=len[i]/2-1;++j)swap(ss[j],ss[len[i]-j-1]); S2[i].init(len[i]); }ll ans = 0; for (int i=1;i<=n;++i){ int m = len[i]; for (int r = 1;r<m;++r) if (S[i].gethash(1,r)==S2[i].gethash(m-r+1,m)) ans+=HT.quiry(S2[i].gethash(1,m-r)); for (int l=m;l>1;--l) if (S[i].gethash(l,m)==S2[i].gethash(1,m-l+1)) ans+=HT.quiry(S2[i].gethash(m-l+2,m)); ans+=HT.quiry(S2[i].gethash(1,m)); }printf("%lld\n",ans); }