leetcode刷题笔记-1163-SAM板子/双指针

本文探讨了如何解决LeetCode上关于寻找字符串中字典序最大的子串的问题,介绍了两种方法:后缀自动机和双指针技巧。后缀自动机法的时间复杂度为O(n*26),而双指针优化后达到O(n)。通过实例代码展示了这两种解题思路。
摘要由CSDN通过智能技术生成

题目链接:https://leetcode-cn.com/problems/last-substring-in-lexicographical-order/

题意

给出一个字符串,求其字典序最大的子串
n < = 1 0 5 n<=10^5 n<=105

题解

解法1:
直接构建后缀自动机/后缀数组,直接跑即可,复杂度 O ( n ∗ 26 ) O(n*26) O(n26)
解法2:
考虑到只需要对比所有后缀,其实就是后缀排序,但只需要找到最大值,所以用双指针枚举后缀,因为长的子串在一直相同的情况一定比短的子串大,所以维护两个起始点判定,如果后面的子串更大就把起始点往后移
维护两个起始点指针L,R,并往后枚举k位相同
1. s [ L + k ] = = s [ R + k ] s[L+k]==s[R+k] s[L+k]==s[R+k]:继续判断
2. s [ L ] < S [ R + k ] s[L]<S[R+k] s[L]<S[R+k]:找到更大的起始点,直接把L移动到R+k
3. s [ L + k ] < s [ R + k ] s[L+k]<s[R+k] s[L+k]<s[R+k]:右侧子串更大,把L移动到R,R移动到R+1
4. s [ L + k ] > s [ R + k ] s[L+k]>s[R+k] s[L+k]>s[R+k]:左侧子串更大,令R++,重新匹配

code

1.SAM解法

#define ms(a) memset(a, 0, sizeof(a))
const int N = 800001;
const int base = 26;

int tr[N][base]; //base很大时使用umap
int link[N];
int len[N];

int cntp = 0;
int last = 0;

void init()
{
    len[0] = 0;
    ms(tr[0]);
    last = 0;
    link[0] = -1;
    cntp = 0;
}

int addp()
{
    int res = ++cntp;
    //is_clo[res] = 0;
    ms(tr[res]);
    return res;
}

int clone(int p, int q)
{
    int res = addp();
    len[res] = len[p] + 1;
    memcpy(tr[res], tr[q], sizeof(tr[res]));
    link[res] = link[q];
    return res;
}

void insert(int ch)
{
    int cur = addp();
    int p = last, q;
    last = cur;
    len[cur] = len[p] + 1;
    while (p != -1 && !tr[p][ch])
    {
        tr[p][ch] = cur;
        p = link[p];
    }
    if (p == -1)
    {
        link[cur] = 0;
        return;
    }
    q = tr[p][ch];
    if (len[q] == len[p] + 1)
    {
        link[cur] = q;
        return;
    }
    int clo = clone(p, q);
    while (p != -1 && tr[p][ch] == q)
    {
        tr[p][ch] = clo;
        p = link[p];
    }
    link[q] = link[cur] = clo;
}
class Solution
{
public:
    string lastSubstring(string s)
    {
        init();
        int n = s.length();
        for (int i = 0; i < n; i++)
        {
            insert(s[i] - 'a');
        }
        string res;
        int p = 0;
        while (1)
        {
            int flag = 0;
            for (int i = 25; i >= 0; i--)
            {
                if (tr[p][i])
                {
                    res.push_back(i + 'a');
                    p = tr[p][i];
                    flag = 1;
                    break;
                }
            }
            if (!flag)
                break;
        }
        return res;
    }
};

2.双指针解法

class Solution {
public:
    string lastSubstring(string s) 
    {
        int L = 0, R = 1, k = 0, n = s.size();
        while(R + k < n)
        {
            if(s[L + k] == s[R + k]) 
            	k++;
            else if(s[L] < s[R + k])
            {
                L = R + k;
                R = L + 1;
                k = 0;
            }
            else if(s[L + k] < s[R + k])
            {
                L = R;
                R++;
                k = 0;
            }
            else
            {
                R++;
                k = 0;
            } 
        }
        return s.substr(L);
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值