Keadin非常苦恼,他马上就要去阿里实习了,但根据公司的规定,在此之前他要为自己取一个在公司内使用的花名,Keadin的取名困难症犯了。他想了一个选出自己花名的规则,但这个规则太复杂所以他想请你帮忙。出于羞耻心和公司的保密要求,Keadin加密了所有字符串,因此你看到的所有字符串都只包含0、1两种字符。
Keadin决定按照以下规则来选择自己的花名,首先列出N个0字符串Si,表示已经被注册的花名,从1到N编号,之后再列出M个0字符串Ti,表示Keadin备选的花名,从1到M编号,他现在想知道,对于他列出的每一个备选花名Ti,有多少Si与其互为前缀,互为前缀是指Si是Ti的前缀,或者Ti是Si的前缀,如果你对前缀的定义不清楚,可以见下面的解释。
注意:在本题中,花名可以重名。
对于一个非空字符串A和一个非空字符串B,当且仅当A从末尾删去若干个字符(可以删除0个,即不删)后与B完全相等时,称B为A的前缀,很显然,一个长度为L的字符串A拥有L个不相同的前缀。例如,若字符串A=abcd,则A的前缀有a,ab,abc,abcd四种。
题解:
对所有的母串建立一颗字典树,然后查询前缀的时候利用这颗字典树就可以大大减少时间和空间复杂度。
在此之前并没有研究过字典树,只知道大概有这么个东西,考试的时候手写了一遍,最后AC了,终极爆爽。
#include<bits/stdc++.h> using namespace std; const int maxn=5e4+100; int N,M; string s[maxn]; string t[maxn]; int tot=0; struct node { int num=0;//表示当前节点的字符串数量 int l=-1; int r=-1;//表示左右孩子,左孩子表示0节点,右孩子表示1节点 }Node[maxn*10]; int tt=0; int ans; void insert (string s) { int i; int u=0; for (int i=0;i<s.length();i++) { if (s[i]=='0') { if (Node[u].l==-1) Node[u].l=++tot; u=Node[u].l; } else { if (Node[u].r==-1) Node[u].r=++tot; u=Node[u].r; } } Node[u].num++; } int cal (string s) { int ans=0; int u=0; for (int i=0;i<s.length();i++) { if (u==-1) return ans; ans+=Node[u].num; if (s[i]=='0') u=Node[u].l; else u=Node[u].r; } if (u==-1) return ans; ans+=Node[u].num; queue<int> q; q.push(u); while (!q.empty()) { int tt=q.front(); q.pop(); if (Node[tt].l!=-1) { ans+=Node[Node[tt].l].num; q.push(Node[tt].l); } if (Node[tt].r!=-1) { ans+=Node[Node[tt].r].num; q.push(Node[tt].r); } } return ans; } int main () { scanf("%d",&N); Node[tot].num=0; Node[tot].l=-1; Node[tot].r=-1; for (int i=1;i<=N;i++) { cin>>s[i]; insert(s[i]); } //if (tot>5e5) while (1); scanf("%d",&M); for (int i=1;i<=M;i++) { cin>>t[i]; ans=cal(t[i]); printf("%d ",ans); } }