【题目】
【分析】
30 pts:哈希+二分
可以用 排序,也可以利用哈希做到 O 比较两个字符串的大小。
具体方法是哈希结合二分找到第一个字符串不同的位置,然后比较这个位置的大小。
结合 ,时间复杂度O 。
另外 30 pts( ):二叉树
此时形成一棵二叉树,任意长者的字符串与 号长者的字符串最多只有 O 处不同。这说明任意两个字符串最多也只有 O 处不同。
于是我们只用比较这 O 个不同的位置,结合 可以做到 O 。
100 pts:主席树
哈希二分的方法其实就是不断的询问字符串的某一部分的哈希值。
我们可以用线段树来维护哈希值,那么每次修改一个位置只需要把父亲的线段树改一个位置。 利用主席树来维护,比较的时候也从主席树的两个根开始递归比较,可以做到 O 。
这里解释一点:代码中线段树在合并哈希值时是倒着合并的,即若左子树是
,右子树是
,那合并上来应该是
,但这不影响答案的正确性,因为哈希只是判断两个串是否相等,比较时还是要递归到叶子才
(改成正着合并也可以)
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define LogN 40
#define base 31
#define ull unsigned long long
using namespace std;
int n,m,t;
char s[N];
ull Pow[N]={1};
int rank[N],root[N];
struct President_Tree
{
ull num;
int lc,rc,len;
#define lc(x) a[x].lc
#define rc(x) a[x].rc
#define len(x) a[x].len
#define num(x) a[x].num
}a[N*LogN];
void build(int &root,int l,int r)
{
root=++t;
len(root)=r-l+1;
if(l==r)
{
num(root)=s[l];
return;
}
int mid=(l+r)>>1;
build(lc(root),l,mid);
build(rc(root),mid+1,r);
num(root)=num(lc(root))+num(rc(root))*Pow[len(lc(root))];
}
void insert(int y,int &x,int l,int r,int pos,char c)
{
x=++t;
a[x]=a[y];
if(l==r)
{
num(x)=c;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) insert(lc(y),lc(x),l,mid,pos,c);
else insert(rc(y),rc(x),mid+1,r,pos,c);
num(x)=num(lc(x))+num(rc(x))*Pow[len(lc(x))];
}
int cmp(int x,int y,int l,int r)
{
if(l==r)
return num(x)<num(y)?-1:1;
int mid=(l+r)>>1;
if(num(lc(x))!=num(lc(y)))
return cmp(lc(x),lc(y),l,mid);
return cmp(rc(x),rc(y),mid+1,r);
}
bool comp(const int &p,const int &q)
{
if(num(root[p])!=num(root[q]))
return cmp(root[p],root[q],1,m)<0;
return p<q;
}
int main()
{
// freopen("z.in","r",stdin);
// freopen("z.out","w",stdout);
int p,pos,i;
scanf("%d%d%s",&n,&m,s+1);
for(i=1;i<=m;++i)
Pow[i]=Pow[i-1]*base;
build(root[1],1,m);
for(i=2;i<=n;++i)
{
scanf("%d%d%s",&p,&pos,s+1);
insert(root[p],root[i],1,m,pos,s[1]);
}
for(i=1;i<=n;++i) rank[i]=i;
sort(rank+1,rank+n+1,comp);
for(i=1;i<=n;++i) printf("%d ",rank[i]);
// fclose(stdin);
// fclose(stdout);
return 0;
}