FZU 2280 Magic(字符串Hash)题解
题意:给你n个字符串,每个字符串有一个值w,有q次询问,一共两种操作:一是“1 x y”表示把第x个串的w变为y;二是“2 x”,输出第x个串能放几次魔法。放魔法的条件是这样:用串x放魔法,如果在1~n个串中,一个串的w不超过x的w并且x是这个串的后缀,则算放了一次魔法。
思路:用Hash每个串,记录w,查询时遍历每个串的后缀是否和x相等并且wi <= wx。这里就是用到了字符串哈希的知识。
参考:
代码:
#include<cstdio> #include<set> #include<map> #include<cmath> #include<stack> #include<vector> #include<queue> #include<cstring> #include<string> #include<sstream> #include<iostream> #include<algorithm> #define ll long long #define ull unsigned long long using namespace std; const int maxn = 1000+10; const ull seed = 131; const int INF = 0x3f3f3f3f; char s[maxn][maxn]; int w[maxn],len[maxn]; ull hash[maxn][maxn]; ull bin[maxn]; //seed的i次方自然溢出结果 void init(){ bin[0] = 1; for(int i = 1;i <= 1000;i++) //预处理 bin[i] = bin[i - 1] * seed; } void HASH(int n){ for(int i = 1;i <= n;i++){ //每个串hash hash[i][0] = 0; for(int j = 1;j <= len[i];j++){ hash[i][j] = hash[i][j - 1] * seed + s[i][j] - 'a'; } } } inline ull getsuff(int i,int length){ //寻找子串hash值(此为后缀) int l = len[i] - length + 1,r = len[i]; return (hash[i][r] - hash[i][l - 1] * bin[r - l + 1]); } int solve(int x,int n){ int ans = 0; for(int i = 1;i <= n;i++){ if(w[i] > w[x]) continue; if(len[i] < len[x]) continue; if(getsuff(i,len[x]) == hash[x][len[x]]){ ans++; } } return ans; } int main(){ int T; int n; init(); scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i = 1;i <= n;i++){ scanf("%s%d",s[i] + 1,&w[i]); len[i] = strlen(s[i] + 1); } HASH(n); int q; int o,x,y; scanf("%d",&q); while(q--){ scanf("%d",&o); if(o == 1){ scanf("%d%d",&x,&y); w[x] = y; } else{ scanf("%d",&x); printf("%d\n",solve(x,n)); } } } return 0; }