题意:两个字符串相似定义为:

1.两个字符串长度相等

2.两个字符串对应位置上至多有一个位置所对应的字符不相同

给定一个字符串\(s\),\(T\)次询问两个子串在给定的规则下是否相似。给定的规则指每次给出一些等价关系,如‘a'=’b',‘b'=’c'(具有传递性)

\(|s|,T \leq 3 \times 10^5\)

题目中的每次询问相当于把一些字符合并成了一些联通块,每个联通块内的字符视为相同。这用并查集合并。

首先考虑询问相等而非相似怎么做。直接的子串对比我们可以直接比较哈希值,但这里要支持合并字符。因此,考虑每个字符对哈希值的贡献。把它除以这个字符的权值后,就相当于这个字符在子串中的出现位置的哈希值,在每个联通块内,这是可以直接相加的。因此,我们只要比较两个子串每个联通块的出现位置是否相同,就能判断是否相等。

再考虑恰好有一位不同的情况。在这种情况下,只有两个联通块在两个子串中出现位置不同,且其哈希值的差值为哈希底数的若干次幂及其相反数。这是可以\(O(1)\)判断的。

时间复杂度\(O((|s| + T )\times26)\)。

#include <bits/stdc++.h>
using namespace std;
const int N = 300010, BAS = 233, MOD = 998244353;
char s[N],t[10];
int has[26][N],n,m,pw[N],uni[30],val[2][30],rec[2],cnt;
int getfa(int pos) {
return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]);
}
unordered_map<int,int> mp;
int gethas(int k,int l,int r) {
return ((has[k][r] - 1ll * has[k][l-1] * pw[r-l+1] % MOD) + MOD) % MOD;
}
void init() {
for (int i = 1 ; i <= 26 ; ++ i)
uni[i] = i;
memset(val,0,sizeof val);
cnt = 0;
}
void solve() {
int k,l1,r1,l2,r2,x,y;
scanf("%d%d%d%d%d",&k,&l1,&r1,&l2,&r2);
init();
for (int i = 1 ; i <= k ; ++ i) {
scanf("%s",t+1);
x = t[1] - 'a' + 1;
y = t[2] - 'a' + 1;
x = getfa(x);
y = getfa(y);
if (x != y) uni[x] = y;
}
for (int i = 0 ; i < 26 ; ++ i)
(val[0][getfa(i+1)] += gethas(i,l1,r1)) %= MOD;
for (int i = 0 ; i < 26 ; ++ i)
(val[1][getfa(i+1)] += gethas(i,l2,r2)) %= MOD;
int key = 0;
for (int i = 1 ; i <= 26 ; ++ i)
key += (val[0][i] != val[1][i]);
if (!key) return (void) (puts("YES"));
if (key > 2) return (void) (puts("NO"));
for (int i = 1 ; i <= 26 ; ++ i) {
if (val[1][i] != val[0][i]) {
x = val[1][i] - val[0][i];
x = (x % MOD + MOD) % MOD;
if (!mp.count((x))) return (void) (puts("NO"));
rec[cnt++] = mp[x];
}
}
if (rec[0] == -rec[1]) puts("YES");
else puts("NO");
}
int main() {
scanf("%s",s+1);
n = strlen(s+1);
for (int i = 0 ; i < 26 ; ++ i) {
for (int j = 1 ; j <= n ; ++ j)
has[i][j] = (1ll * has[i][j-1] * BAS + (s[j] == 'a' + i)) % MOD;
}
pw[0] = 1;
for (int i = 1 ; i <= n ; ++ i)
pw[i] = 1ll * pw[i-1] * BAS % MOD;
for (int i = 0 ; i <= n ; ++ i)
mp[pw[i]] = i + 1, mp[MOD - pw[i]] = -i - 1;
scanf("%d",&m);
for (int i = 1 ; i <= m ; ++ i)
solve();
return 0;
}

小结:解题关键就在于拆分每个字符的贡献,以实现合并。这利用了哈希值容易拆分合并的性质。

【做题】51NOD1753 相似子串——哈希的更多相关文章

  1. noip做题记录+挑战一句话题解?

    因为灵巧实在太弱辽不得不做点noip续下命QQAQQQ 2018 积木大赛/铺设道路 傻逼原题? 然后傻逼的我居然检查了半天是不是有陷阱最后花了差不多一个小时才做掉我做过的原题...真的傻逼了我:( ...

  2. POI做题记录

    嘿嘿,偷学一波! 由于博主做的题比较少,所以没按年份整理,直接按照做题时间放上来了. 2020年9月20日 [POI2013]LUK-Triumphal arch 给你一颗\(n\)个点的树(\(n\ ...

  3. SDOI2016 R1做题笔记

    SDOI2016 R1做题笔记 经过很久很久的时间,shzr终于做完了SDOI2016一轮的题目. 其实没想到竟然是2016年的题目先做完,因为14年的六个题很早就做了四个了,但是后两个有点开不动.. ...

  4. 【做题】BZOJ2342 双倍回文——马拉车&并查集

    题意:有一个长度为\(n\)的字符串,求它最长的子串\(s\)满足\(s\)是长度为4的倍数的回文串,且它的前半部分和后半部分都是回文串. \(n \leq 5 \times 10^5\) 首先,显然 ...

  5. Sam做题记录

    Sam做题记录 Hihocoder 后缀自动机二·重复旋律5 求一个串中本质不同的子串数 显然,答案是 \(\sum len[i]-len[fa[i]]\) Hihocoder 后缀自动机三·重复旋律 ...

  6. 退役III次后做题记录(扯淡)

    退役III次后做题记录(扯淡) CF607E Cross Sum 计算几何屎题 直接二分一下,算出每条线的位置然后算 注意相对位置这个不能先搞出坐标,直接算角度就行了,不然会卡精度/px flag:计 ...

  7. 题解 [51nod1753] 相似子串

    题解 [51nod1753] 相似子串 题面 解析 先考虑相等的时候怎么办, 我们考虑求出每个字母的贡献,这样字母相等的问题就可以用并查集来解决. 具体来说,我们先对于每个字母,把S中等于它的标为1, ...

  8. NOIP2016考前做题(口胡)记录

    NOIP以前可能会持续更新 写在前面 NOIP好像马上就要到了,感觉在校内训练里面经常被虐有一种要滚粗的感觉(雾.不管是普及组还是提高组,我都参加了好几年了,结果一个省一都没有,今年如果还没有的话感觉 ...

  9. SAM 做题笔记(各种技巧,持续更新,SA)

    SAM 感性瞎扯. 这里是 SAM 做题笔记. 本来是在一篇随笔里面,然后 Latex 太多加载不过来就分成了两篇. 标 * 的是推荐一做的题目. trick 是我总结的技巧. I. P3804 [模 ...

随机推荐

  1. Rest概念学习

    参考文章 http://www.cnblogs.com/shanyou/archive/2012/05/12/2496959.html http://www.cnblogs.com/loveis715 ...

  2. awk命令学习(1)

    awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大.简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各 ...

  3. 关于django编码问题所导致的502错误

    在使用django开发的时候,访问网站出现了502错误,次数也比较平繁 uwsgi+nginx+django 在命令行下提示如下错误 : File "/opt/apps/python3/li ...

  4. 用Django实现Video页面分类查询

    Model表创建,Url映射,Views函数处理,Html生成 根据上图,视频方向与视频分类是多对多的关系,视频分类与视频信息是一对多的关系,难度级别是单一的查询条件(与之前俩者并无关系) Model ...

  5. SourceTree 使用

    删除分支 新建一个分支 A 后,要想把 A 分支删除掉,只需跳到另一个分支上去,选中 A 分支然后右击,在弹出的菜单栏中选中 [删除 A 分支]即可: 在分支下建一个文件夹 左上的然后弹出选框,在[新 ...

  6. Nginx配置基础-location

    location表达式类型 ~ 表示执行一个正则匹配,区分大小写~* 表示执行一个正则匹配,不区分大小写^~ 表示普通字符匹配.使用前缀匹配.如果匹配成功,则不再匹配其他location.= 进行普通 ...

  7. jack welch:“你们知道了,但是我们做到了”

    一.我们发现,只要我们敢于相信自己,敢于朝着那些看似不可能的目标不懈努力,最终会如愿以偿,个人的领袖形象也将因此而确立. 二.一个领导者必须要有魄力.对我来说,这就是一个人能否领导一项业务的分水岭. ...

  8. Spring源码阅读(三)

    上一讲我们谈到单例生产关键方法getSingleton.getSingleton方法由DefaultSingletonBeanRegistry类实现.我们的抽象工厂AbstractBeanFactor ...

  9. sqoop从hive导入数据到mysql时出现主键冲突

    今天在将一个hive数仓表导出到mysql数据库时出现进度条一直维持在95%一段时间后提示失败的情况,搞了好久才解决.使用的环境是HUE中的Oozie的workflow任何调用sqoop命令,该死的o ...

  10. django 常用方法总结 < 手写分页-上传头像-redis缓存,排行 ...>

    1.不使用自带模块<Paginator>的手写分页功能views.pydef post_list(request): page = request.GET.get('page', 1) # ...