【HNOI2014】抄卡组
题面
题解
如果所有的字符串都有通配符,那么只要比较不含通配符的前缀和后缀就可以了。
否则一定有一个串没有通配符。找出这个字符串,然后将所有串与这个串匹配,通配符将\(B\)分成一段一段在\(A\)上匹配,然后越早出现越好,这里用\(\mathrm{KMP, hash}\)都可以
讲起来容易,但是写起来的话就有点复杂了
时间复杂度:\(\mathrm{O}(\sum \mid S_i\mid)\)
代码复杂度:\(\mathrm{O}(\infty)\)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#include<string>
#define RG register
const unsigned long long X(60923);
unsigned long long val[10000010];
const int maxn(100010);
std::string s[maxn], s1[maxn];
std::vector<unsigned long long> h[maxn];
std::vector<int> pos[maxn];
inline int cmp(const std::string &lhs, const std::string &rhs)
{ return lhs.length() < rhs.length(); }
int T, n;
void calc(int i)
{
pos[i].clear(), h[i].clear();
pos[i].push_back(-1), h[i].push_back(0);
for(std::string::iterator it = s[i].begin(); it != s[i].end(); ++it)
{
h[i].push_back(h[i].back() * X + *it);
if(*it == '*') pos[i].push_back(it - s[i].begin());
}
pos[i].push_back(s[i].length());
}
inline unsigned long long Hash(const std::vector<unsigned long long> &vec,
int l, int r)
{
++l, ++r;
return vec[r] - vec[l - 1] * val[r - l + 1];
}
bool check(int x, int y)
{
int lenx = s[x].length(), leny = s[y].length();
if(s[y].find('*') != std::string::npos)
std::swap(x, y), std::swap(lenx, leny);
if(s[x].find('*') == std::string::npos
&& s[y].find('*') == std::string::npos)
return Hash(h[x], 0, s[x].length() - 1) == Hash(h[y], 0, s[y].length() - 1);
else
{
std::string A = ""; std::string::size_type p = 0;
for(RG int i = 1; i < pos[x].size(); i++)
{
int tpos = p, len = pos[x][i] - pos[x][i - 1] - 1;
while(tpos + len - 1 < s[y].length() &&
Hash(h[x], pos[x][i - 1] + 1, pos[x][i] - 1)
!= Hash(h[y], tpos, tpos + len - 1)) ++tpos;
if(tpos + len - 1 >= s[y].length()) return false;
if(tpos != 0 && p == 0) return false;
p = tpos + len;
}
return true;
}
}
void Doit()
{
int pos = -1;
for(RG int i = 1; i <= n; i++) calc(i);
for(RG int i = 1; i <= n; i++)
if(s[i].find('*') == std::string::npos) { pos = i; break; }
if(pos == -1)
{
for(RG int i = 1; i <= n; i++)
{
s1[i] = "";
for(RG int j = 0; j < s[i].length(); j++)
if(s[i][j] == '*') break;
else s1[i] += s[i][j];
}
std::sort(s1 + 1, s1 + n + 1, cmp);
for(RG int i = 2; i <= n; i++)
{
for(RG int j = 0; j < s1[i - 1].length(); j++)
if(s1[i][j] != s1[i - 1][j])
return (void)(std::cout << 'N' << std::endl);
}
for(RG int i = 1; i <= n; i++)
{
s1[i] = "";
for(RG int j = s[i].length() - 1; ~j; j--)
if(s[i][j] == '*') break;
else s1[i] += s[i][j];
}
std::sort(s1 + 1, s1 + n + 1, cmp);
for(RG int i = 2; i <= n; i++)
{
for(RG int j = 0; j < s1[i - 1].length(); j++)
if(s1[i][j] != s1[i - 1][j])
return (void)(std::cout << 'N' << std::endl);
}
}
else for(RG int i = 1; i <= n; i++)
{
if(i == pos) continue;
// std::cout << i << std::endl;
if(!check(i, pos)) return (void)(std::cout << 'N' << std::endl);
}
std::cout << 'Y' << std::endl;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin >> T, val[0] = 1;
for(RG int i = 1; i <= 10000000; i++) val[i] = val[i - 1] * X;
while(T--)
{
std::cin >> n;
for(RG int i = 1; i <= n; i++)
std::cin >> s[i];
Doit();
}
return 0;
}
【HNOI2014】抄卡组的更多相关文章
- bzoj3574[Hnoi2014]抄卡组
http://www.lydsy.com/JudgeOnline/problem.php?id=3574 我们发现如果所有的字符串都有*,那么只需要比较他们的“前缀”和“后缀”相同即可.“前缀”指第一 ...
- luogu P3234 [HNOI2014]抄卡组
传送门 nmdwsm 自己看吧,不想写了qwq 垃圾代码如下 和题解完全不一样 #define LL long long #define uLL unsigned long long #define ...
- 【LG3234】[HNOI2014]抄卡组
题面 题解 分三种情况: 若所有串都没有通配符,直接哈希比较即可. 若所有串都有通配符, 把无通配符的前缀 和 无通配符的后缀哈希后比较即可. 中间部分由于通配符的存在,一定可以使所有串匹配. 若部分 ...
- BZOJ3574 HNOI2014抄卡组(哈希)
容易发现通配符中间的部分可以任意匹配,会造成的无法匹配的仅仅是前后缀,前缀和后缀可以分别独立处理.如果字符串均有通配符,只需要按前/后缀长度排序然后暴力匹配就可以了. 问题在于存在无通配符的字符串.显 ...
- [HNOI2014]抄卡组
[Luogu3234] [LOJ2208] 题解及代码 锻炼哈希码力的一道题 , 具体细节见代码 #include<cstdio> #include<cstring> #inc ...
- 洛谷P3234 抄卡组 [HNOI2014] 字符串hash
正解:字符串hash 解题报告: 传送门! 字符串hash是字符串匹配中很常见的一个方法,原理也很好懂,这里就不做太多阐述辣有时间放到hash笔记里面去QAQ 题意不说了挺好理解的,自带一句话概括好评 ...
- 【LOJ6254】最优卡组 堆(模拟搜索)
[LOJ6254]最优卡组 题面 题解:常用的用堆模拟搜索套路(当然也可以二分).先将每个卡包里的卡从大到小排序,然后将所有卡包按(最大值-次大值)从小到大排序,并提前处理掉只有一张卡的卡包. 我们将 ...
- HearthBuddy卡组
偶数萨 手打两天已上传说,各位加油 欧洲牧羊人 ### 火元素换艾雅# 职业:萨满祭司# 模式:狂野模式## 2x (2) 图腾魔像 # 2x (2) 大漩涡传送门 # 2x (2 ...
- 服务器&阵列卡&组raid 5
清除raid信息后,机器将会读不到系统, 后面若进一步操作处理, raid信息有可能会被初始化掉,那么硬盘数据就有可能会被清空, 导致数据丢失, 否则如果只是清除raid信息,重做raid是可以还原系 ...
随机推荐
- ASP.Net WebAPI的返回值
Asp.Net WebAPI服务函数的返回值主要可以分为void.普通对象.HttpResponseMessag.IHttpActionResult e四种,本文这里简单的介绍一下它们的区别. 一.返 ...
- MySQL慢日志简介及Anemometer工具介绍
作者:王航威 - fordba.com 来源:http://fordba.com/box-anemometer-visual-mysql-slow.html,叶师傅对原文内容略有调整 备注:王航威是知 ...
- iOS设计模式 - 备忘录
iOS设计模式 - 备忘录 原理图 说明 1. 在不破坏封装的情况下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态 2. 本人已经将创建状态与恢复状态 ...
- [翻译] ZLHistogramAudioPlot
ZLHistogramAudioPlot A hardware-accelerated audio visualization view using EZAudio, inspired by Audi ...
- Python学习---xml文件的解析[beautifulsoup4模块学习]
1.1. 安装beautifulsoup4 pip install beautifulsoup4 [更多参考]https://blog.csdn.net/sunhuaqiang1/article/de ...
- September 12th 2017 Week 37th Tuesday
Failure is the fog through which we glimpse triumph. 失败是迷雾,穿过它,我们就可以瞥见光明. Sometimes the fog may be t ...
- ArcGIS Engine开发基础总结(一)
标准Engine功能 地图浏览 地图制作 数据查询 数据分析 及 所有的开发控件 —MapControl, PageLayout, Toolbar, TOC, ArcReader 对所有矢量和栅 ...
- AOP-切面是如何织入到目标对象中的
切面是如何织入到目标对象中的???这大概是每个人在学习AOP的过程中都会产生的疑问吧. 当我们在调用目标方法时候,也就是通过代理对象调用目标方法的时候,比如:JdkDynamicAopProxy会通过 ...
- python第十八课——常用内置函数
常用内置函数:round(): print(round(3.14),round(3.99)) print(round(3145.926,-2),round(413.575,2)) abs(): pri ...
- python第十四课--排序及自定义函数之自定义函数(案例四)
整理:4中最常见的自定义函数模型1).无参无返回值2).无参有返回值3).有参无返回值4).有参有返回值 #定义无参无返回值自定义函数 def func1(): print('hello method ...