答题卡
答题卡
题目大意
给定 \(n\) 个字符串,问有多少个二元组 \((i,j)\) 满足 字符串 \(i\) 的长度小于字符串 \(j\) 的长度,且字符串 \(j\) 能够通过下述两种操作变为字符串 \(i\) :
-
删除第一个字母。
-
删除第二个字母。
分析
首先对于这道题有一个非常显然的性质,一个成立的二元组必然满足一下条件:
-
字符串 \(i\) 去掉首字母后的子串能够在字符串 \(j\) 的末端找到。
-
字符串 \(i\) 的首字母能够在 \(j\) 与 \(i\) 相同的末端子串前找到。
之后可以考虑将所有的串全部倒序丢到一棵 \(trie\) 树上,预处理记录两个信息,一个是拥有这个前缀的字符串的数量,而是在这个前缀后有字母 \(x\) 的字符串数量。
之后进行匹配即可。
code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10,M=1e6+10;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w*=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int n,ans;
int tot=1,x[M],y[M][26],trie[M][26];
string s[N];
inline void insert(string add)
{
int l=add.size(),p=1;
for(register int i=l-1;i>=0;i--){
int ch=add[i]-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;
p=trie[p][ch];
x[p]++;
}
}
struct node{
int id,ch;
}st[M];
int top;
int num[26];
inline void initial(string data)
{
memset(num,0,sizeof(num));
top=0;
int l=data.size(),p=1;
for(register int i=l-1;i>=0;i--){
int ch=data[i]-'a';
st[++top].ch=ch,st[top].id=p;
num[ch]++;
p=trie[p][ch];
}
for(register int i=1;i<=top;i++){
for(register int j=0;j<=25;j++){
if(num[j]) y[st[i].id][j]++;
}
num[st[i].ch]--;
}
}
inline void Solve(string ck)
{
int l=ck.size(),st=ck[0]-'a',p=1;
for(register int i=l-1;i>=1;i--){
int ch=ck[i]-'a';
p=trie[p][ch];
if(x[p]==1) { p=0; break; }
if(!p) break;
}
if(p) ans=ans+min(x[p]-1,y[p][st]-1);
}
signed main()
{
n=read();
x[1]=n;
for(register int i=1;i<=n;i++){
cin>>s[i];
insert(s[i]);
}
for(register int i=1;i<=n;i++){
initial(s[i]);
}
//cout<<y[4][2]<<endl;
for(register int i=1;i<=n;i++){
Solve(s[i]);
}
printf("%lld\n",ans);
return 0;
}