Pref 社论

题面

一个长度为 k 字符串序列 s 是好的,当且仅当 1i<k,有 Bi 既是 Bi+1 的前缀,又是 Bi+1 的后缀 .

给一个字符串序列 A,求其最长好子序列 .


数据范围:k|Ak|2×106

时限 2 s,空限 512 MB .

题解

算法 1

考虑 dp .

dpi 表示以 Ai 结尾的最长好序列,于是可以暴力转移 .

时间复杂度 O(n3),期望 2040pts .

算法 2

考虑加速算法 1 中转移过程 .

字符串 Hash 处理每个子串的 border,同时构建映射 M:borderdp .

一轮 dp 可以线性完成,映射可以考虑两种实现方式

方式编号 表现 1 表现 2 时间复杂度
1 std::map 平衡树 O(nlogn)
2 std::unordered_map Hash Table O(n)

期望 100pts .

算法 3(标答)

s¯s 逆序排成的字符串 .

于是 st 的后缀等价于 s¯t¯ 的前缀 .

现在我们有两个前缀关系,建 Trie 树并且在 Trie 树上 dp 即可 .

期望 100pts .

代码

算法 1

20pts(by jijidawang)

using namespace std;
typedef long long ll;
const int N = 1e6 + 500;
int n;
ll p, dp[N];
string s[N];
inline bool pure_chk(string a, string b)
{
	int la = a.length(), lb = b.length();
	if (la > lb) return false;
	for (int i=0; i<la; i++)
		if (a[i] != b[i]) return false;
	return true;
}
inline bool chk(string a, string b)
{
	bool ans = pure_chk(a, b);
	reverse(b.begin(), b.end());
	return ans & pure_chk(a, b);
}
int main()
{
	scanf("%d", &n);
	for (int i=1; i<=n; i++) cin >> s[i];
	dp[1] = 1;
	for (int i=2; i<=n; i++)
		for (int j=1; j<i; j++)
			if (chk(s[j], s[i])) dp[i] = max(dp[i], dp[j]+1);
	ll ans = 0;
	for (int i=1; i<=n; i++) ans = max(ans, dp[i]);
	printf("%lld\n", ans);
	return 0;
}

40pts(by Rolling_Star)

using namespace std;

int n,dp[2000001];
string s[2000001];
bool flag;
inline bool check(int x,int y);

int main()
{
    cin>>n;
    for(register int i=1;i<=n;i++)
        cin>>s[i];
    int ans=0;
    dp[1]=1;
    for(register int i=2;i<=n;++i)
    {
        dp[i]=1;
        for(register int j=1;j<=i-1;++j)
            if(s[i].size()>=s[j].size())
                if(check(i,j))
                    dp[i]=max(dp[i],dp[j]+1);
        ans=max(ans,dp[i]);
    }
    cout<<ans;
}

inline bool check(int x,int y)
{
    for(register int i=0;i<s[y].size();++i)
        if(s[y][i]!=s[x][i])
            return false;
    for(register int i=s[x].size()-s[y].size(),j=0;j<s[y].size();++i,++j)
        if(s[y][j]!=s[x][i])
            return false;
    return true;
}

算法 2

using namespace std;
const int N = 1e6 + 500;
typedef long long ll;
typedef char str[N];
const ll P = 1e9+7, base = 131;
int n;
ll pb[N];
str s;
map<ll, int> dp;
int main()
{
	scanf("%d", &n);
	pb[0] = 1;
	for (int i=1; i<N; i++) pb[i] = pb[i-1] * base % P;
	int ans = 0;
	for (int i=1; i<=n; i++)
	{
		scanf("%s", s+1); int l = strlen(s+1);
		ll p=0, ss=0; int H=0;
		for (int j=1; j<=l; j++)
		{
			p = (p*base % P + s[j]) % P;
			ss = (ss + s[l-j+1]*pb[j-1] % P) % P;
			if (p == ss) H = max(H, dp[p]);
		}
		dp[p] = max(dp[p], H+1);
		ans = max(ans, dp[p]);
	} printf("%d\n", ans);
	return 0;
}

算法 3

posted @   yspm  阅读(63)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
😅​
点击右上角即可分享
微信分享提示