答题卡

答题卡

题目大意

给定 \(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;
}
posted @ 2021-09-24 21:23  ╰⋛⋋⊱๑落叶๑⊰⋌⋚╯  阅读(60)  评论(0编辑  收藏  举报