蒸鱼的一个简单签到题
发布时间: 2017年6月11日 17:59 最后更新: 2017年6月11日 18:10 时间限制: 1000ms 内存限制: 128M
描述
这天蒸鱼老师碰到了暴力凡的一个题,“这题肯定能暴力” 这个想法出现在了他的脑海。
但是julyc鉴于某凡的暴力过算我输这个根本理念,怎么可能让你随随便便过呢?
于是,他将这个题做了平衡树套替罪羊树套喜羊羊树套圣诞树套动态仙人掌化处理。
但是他良心发现,感觉处理完了好像有点难?
于是把这个题经过哈希套后缀自动机套AC自动机套LCT套莫队算法优化后,再给你丢了一堆神秘字符串。
julyc会给你n个字符串,然后让你找出其中所有的蒸鱼字符串。
蒸鱼字符串的定义是对于字符串s[i](1<=i<=n),如果存在一个j(1<=j< i)使得s[j]不是s[i]的子串,那么我们就称这个字符串为蒸鱼字符串。
输入
第一行一个整数t代表数据组数
每组数据中
第二行一个整数n
接下来输入n个字符串n<=1000
保证每个字符串长度不超过2000
输出
每组数据先输出满足条件的字符串数量,然后第二行从小到大输出所有满足条件的字符串编号。如果没有满足的则输出-1。
样例输入1 复制
4
5
ab
abc
zabc
abcd
zabcd
4
you
lovinyou
aboutlovinyou
allaboutlovinyou
5
de
def
abcd
abcde
abcdef
3
a
ba
ccc
样例输出1
1
4
-1
2
3 4
1
3
哎。。用了个ac自动机超时了。。因为查询太多了。。。还有失配指针的构造太多还得每次复原,所以ac自动机是行不通的。。但是用kmp可以,为什么呢,因为可以剪枝,出现过的能完全上面的串匹配的先清空所有的串再入队,但是别的就是直接入队。代码时间也很简单
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7+100;
typedef long long ll;
char ss[1010][2020];
const int maxn=1010;
int f[1010][2020];
void getfill(char *s,int t)
{
int ll= strlen(s);
for(int i=1;i<ll;i++)
{
int j=f[t][i];
while(j && s[i]!=s[j])
j=f[t][j];
f[t][i+1]=(s[i]==s[j])?j+1:0;
}
}
int find(char *a,char *s,int t)
{
int ans=0;
int j=0;
int ll=strlen(a);
int ll1=strlen(s);
for(int i=0;i<ll;i++)
{
while(j && a[i]!=s[j])
j=f[t][j];
if(a[i]==s[j])
j++;
if(j==ll1){
return 1;
}
}
return 0;
}
vector<int> daan;
set<int> st;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
daan.clear();
st.clear();
for(int i=1;i<=n;i++)
{
scanf(" %s",ss[i]);
int flag=0;
set<int>::iterator it;
for(set<int>::iterator iter=st.begin();iter!=st.end();)
{
if(find(ss[i],ss[*iter],*iter))
{
it=iter;
iter++;
st.erase(it);
}
else iter++;
}
getfill(ss[i],i);
if(!st.empty()) daan.push_back(i);
st.insert(i);
}
if(daan.size())
{
int hh=daan.size();
printf("%d\n",hh );
printf("%d",daan[0] );
for(int i=1;i<hh;i++)
printf(" %d",daan[i] );
}
else printf("-1");
printf("\n");
}
}