【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是可以还原系 ...
随机推荐
- 修改UIView的默认Layer后,修改View的值会动态修改Layer的值
修改UIView的默认Layer后,修改View的值会动态修改Layer的值 效果图: 如上图所示,当我们修改了一个UIView的子类中的Layer内置类型时(如上图中我们将CALayer直接替换成了 ...
- [翻译] TransitionKit
TransitionKit https://github.com/blakewatters/TransitionKit A simple, elegantly designed block based ...
- C++项目规范
https://segmentfault.com/a/1190000007659754
- win7 64 &vs2010 与 opengl配置
http://blog.csdn.net/lixam/article/details/7618015 http://blog.sina.com.cn/s/blog_7745fc8601017m36.h ...
- __iter__的有无
迭代器和生成器 1.迭代器 我们之前⼀直在⽤可迭代对象进⾏迭代操作. 那么到底什么是可迭代对象.⾸先我们先回顾⼀下⽬前我们所熟知的可迭代对象有哪些: str, list, tuple, dict, s ...
- 什么是 .live()
很多开发者都知道jQuery的.live()方法,他们大部分知道这个函数做什么,但是并不知道是怎么实现的,所以用的并不那么舒适.而且他们却从未听过还有解除绑定的.live()事件的.die()方法.即 ...
- 51nod 1275 连续子段的差异
题目看这里 若[i,j]符合要求,那么[i,j]内的任何连续的子段都是符合要求的.我们可以枚举i,找到能合格的最远的j,然后ans+=(j-i+1). 那么问题就转换成了:在固定i的情况下,如何判断j ...
- map filter 的func 放在前面
map filter 的func 放在前面 sorted 在后 ( iter.. , key=function')
- Mac Item2 设置别名 永久生效
使用 Item2 终端, 设置 别名的时候, 按照 网上的说法, 是 去 修改 用户目录下的 .bashrc 或者 .bash_profile 这两个文件都可以, 把 alias 写在 这两 ...
- 检查oracle用户默认密码的账户
1. 检查使用默认用户密码的账号 --11g 通过数据字典SYS.DEFAULT_PWD$或视图DBA_USERS_WITH_DEFPWD select u.username, u.account_s ...