Problem:
Problem M: 1333
Problem
長さ$N$の文字列$S$が与えられる。
  以下のクエリを$Q$回処理せよ。
クエリ
$S[L: R]$を$S$の$L$文字目から$R$文字目まで（両端を含む）からなる文字列とする。
$ S[L: R] $を適当な文字列$A,B,C,X$を用いて$AXBXCX(1 \leq |A|,|B|,|C|,|X|)$ と表すことを考え、そのような$X$の中で最長のものの長さを出力する。
ただし、そのような$X$が存在しない場合は代わりに0を出力する。
Input
入力は以下の形式で与えられる。
$N$ $Q$
$S$
$L_1$ $R_1$
$L_2$ $R_2$
$\vdots$
$L_Q$ $R_Q$
$N,Q,L,R$はすべて整数で与えられる。
1行目に$N$, $Q$が空白区切りで与えられる。
2行目に文字列$S$が与えられる。
2+$i(1\leq i \leq Q)$行目に$L_i$, $R_i$が空白区切りで与えられる。これらは$i$番目のクエリにおける$L,R$を表す。
Constraints
入力は以下の条件を満たす。
$1 \leq N, Q\leq 2 \times 10^5 $
$S$の各文字は小文字アルファベットからなる
$1 \leq L_i \leq R_i \leq N $
Output
各クエリについて、最長の$X$の長さを一行に出力せよ。
Sample Input 1
12 3
itisansansan
1 12
5 12
6 7
Sample Output 1
2
1
0
一つ目のクエリにおいて、$A=itis, B=s, C=s, X=an$とおくと、$S[1:12]=AXBXCX$となる。
Sample Input 2
20 2
sensanbyakusanjuusan
1 20
1 14
Sample Output 2
3
1
Sample Input 3
21 6
aaaabaaaabaaaaaaaaaab
1 21
10 21
10 18
4 16
11 21
1 6
Sample Output 3
4
0
2
2
0
1

Submitted solutions:

code_snippet: #line 2 "/Users/noya2/Desktop/Noya2_library/template/template.hpp"
using namespace std;

#include<bits/stdc++.h>
#line 1 "/Users/noya2/Desktop/Noya2_library/template/inout_old.hpp"
namespace noya2 {

template <typename T, typename U>
ostream &operator<<(ostream &os, const pair<T, U> &p){
    os << p.first << " " << p.second;
    return os;
}
template <typename T, typename U>
istream &operator>>(istream &is, pair<T, U> &p){
    is >> p.first >> p.second;
    return is;
}

template <typename T>
ostream &operator<<(ostream &os, const vector<T> &v){
    int s = (int)v.size();
    for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i];
    return os;
}
template <typename T>
istream &operator>>(istream &is, vector<T> &v){
    for (auto &x : v) is >> x;
    return is;
}

void in() {}
template <typename T, class... U>
void in(T &t, U &...u){
    cin >> t;
    in(u...);
}

void out() { cout << "\n"; }
template <typename T, class... U, char sep = ' '>
void out(const T &t, const U &...u){
    cout << t;
    if (sizeof...(u)) cout << sep;
    out(u...);
}

template<typename T>
void out(const vector<vector<T>> &vv){
    int s = (int)vv.size();
    for (int i = 0; i < s; i++) out(vv[i]);
}

struct IoSetup {
    IoSetup(){
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(15);
        cerr << fixed << setprecision(7);
    }
} iosetup_noya2;

} // namespace noya2
#line 1 "/Users/noya2/Desktop/Noya2_library/template/const.hpp"
namespace noya2{

const int iinf = 1'000'000'007;
const long long linf = 2'000'000'000'000'000'000LL;
const long long mod998 =  998244353;
const long long mod107 = 1000000007;
const long double pi = 3.14159265358979323;
const vector<int> dx = {0,1,0,-1,1,1,-1,-1};
const vector<int> dy = {1,0,-1,0,1,-1,-1,1};
const string ALP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string alp = "abcdefghijklmnopqrstuvwxyz";
const string NUM = "0123456789";

void yes(){ cout << "Yes\n"; }
void no(){ cout << "No\n"; }
void YES(){ cout << "YES\n"; }
void NO(){ cout << "NO\n"; }
void yn(bool t){ t ? yes() : no(); }
void YN(bool t){ t ? YES() : NO(); }

} // namespace noya2
#line 2 "/Users/noya2/Desktop/Noya2_library/template/utils.hpp"

#line 6 "/Users/noya2/Desktop/Noya2_library/template/utils.hpp"

namespace noya2{

unsigned long long inner_binary_gcd(unsigned long long a, unsigned long long b){
    if (a == 0 || b == 0) return a + b;
    int n = __builtin_ctzll(a); a >>= n;
    int m = __builtin_ctzll(b); b >>= m;
    while (a != b) {
        int mm = __builtin_ctzll(a - b);
        bool f = a > b;
        unsigned long long c = f ? a : b;
        b = f ? b : a;
        a = (c - b) >> mm;
    }
    return a << std::min(n, m);
}

template<typename T> T gcd_fast(T a, T b){ return static_cast<T>(inner_binary_gcd(std::abs(a),std::abs(b))); }

long long sqrt_fast(long long n) {
    if (n <= 0) return 0;
    long long x = sqrt(n);
    while ((x + 1) * (x + 1) <= n) x++;
    while (x * x > n) x--;
    return x;
}

template<typename T> T floor_div(const T n, const T d) {
    assert(d != 0);
    return n / d - static_cast<T>((n ^ d) < 0 && n % d != 0);
}

template<typename T> T ceil_div(const T n, const T d) {
    assert(d != 0);
    return n / d + static_cast<T>((n ^ d) >= 0 && n % d != 0);
}

template<typename T> void uniq(std::vector<T> &v){
    std::sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
}

template <typename T, typename U> inline bool chmin(T &x, U y) { return (y < x) ? (x = y, true) : false; }

template <typename T, typename U> inline bool chmax(T &x, U y) { return (x < y) ? (x = y, true) : false; }

template<typename T> inline bool range(T l, T x, T r){ return l <= x && x < r; }

} // namespace noya2
#line 8 "/Users/noya2/Desktop/Noya2_library/template/template.hpp"

#define rep(i,n) for (int i = 0; i < (int)(n); i++)
#define repp(i,m,n) for (int i = (m); i < (int)(n); i++)
#define reb(i,n) for (int i = (int)(n-1); i >= 0; i--)
#define all(v) (v).begin(),(v).end()

using ll = long long;
using ld = long double;
using uint = unsigned int;
using ull = unsigned long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
using pil = pair<int,ll>;
using pli = pair<ll,int>;

namespace noya2{

/*　~ (. _________ . /)　*/

}

using namespace noya2;


#line 2 "c.cpp"

#line 2 "/Users/noya2/Desktop/Noya2_library/string/suffix_array.hpp"

#line 8 "/Users/noya2/Desktop/Noya2_library/string/suffix_array.hpp"

// atcoder/string

namespace noya2 {

namespace internal {

std::vector<int> sa_naive(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n);
    std::iota(sa.begin(), sa.end(), 0);
    std::sort(sa.begin(), sa.end(), [&](int l, int r) {
        if (l == r) return false;
        while (l < n && r < n) {
            if (s[l] != s[r]) return s[l] < s[r];
            l++;
            r++;
        }
        return l == n;
    });
    return sa;
}

std::vector<int> sa_doubling(const std::vector<int>& s) {
    int n = int(s.size());
    std::vector<int> sa(n), rnk = s, tmp(n);
    std::iota(sa.begin(), sa.end(), 0);
    for (int k = 1; k < n; k *= 2) {
        auto cmp = [&](int x, int y) {
            if (rnk[x] != rnk[y]) return rnk[x] < rnk[y];
            int rx = x + k < n ? rnk[x + k] : -1;
            int ry = y + k < n ? rnk[y + k] : -1;
            return rx < ry;
        };
        std::sort(sa.begin(), sa.end(), cmp);
        tmp[sa[0]] = 0;
        for (int i = 1; i < n; i++) {
            tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
        }
        std::swap(tmp, rnk);
    }
    return sa;
}

// SA-IS, linear-time suffix array construction
// Reference:
// G. Nong, S. Zhang, and W. H. Chan,
// Two Efficient Algorithms for Linear Time Suffix Array Construction
template <int THRESHOLD_NAIVE = 10, int THRESHOLD_DOUBLING = 40>
std::vector<int> sa_is(const std::vector<int>& s, int upper) {
    int n = int(s.size());
    if (n == 0) return {};
    if (n == 1) return {0};
    if (n == 2) {
        if (s[0] < s[1]) {
            return {0, 1};
        } else {
            return {1, 0};
        }
    }
    if (n < THRESHOLD_NAIVE) {
        return sa_naive(s);
    }
    if (n < THRESHOLD_DOUBLING) {
        return sa_doubling(s);
    }

    std::vector<int> sa(n);
    std::vector<bool> ls(n);
    for (int i = n - 2; i >= 0; i--) {
        ls[i] = (s[i] == s[i + 1]) ? ls[i + 1] : (s[i] < s[i + 1]);
    }
    std::vector<int> sum_l(upper + 1), sum_s(upper + 1);
    for (int i = 0; i < n; i++) {
        if (!ls[i]) {
            sum_s[s[i]]++;
        } else {
            sum_l[s[i] + 1]++;
        }
    }
    for (int i = 0; i <= upper; i++) {
        sum_s[i] += sum_l[i];
        if (i < upper) sum_l[i + 1] += sum_s[i];
    }

    auto induce = [&](const std::vector<int>& lms) {
        std::fill(sa.begin(), sa.end(), -1);
        std::vector<int> buf(upper + 1);
        std::copy(sum_s.begin(), sum_s.end(), buf.begin());
        for (auto d : lms) {
            if (d == n) continue;
            sa[buf[s[d]]++] = d;
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        sa[buf[s[n - 1]]++] = n - 1;
        for (int i = 0; i < n; i++) {
            int v = sa[i];
            if (v >= 1 && !ls[v - 1]) {
                sa[buf[s[v - 1]]++] = v - 1;
            }
        }
        std::copy(sum_l.begin(), sum_l.end(), buf.begin());
        for (int i = n - 1; i >= 0; i--) {
            int v = sa[i];
            if (v >= 1 && ls[v - 1]) {
                sa[--buf[s[v - 1] + 1]] = v - 1;
            }
        }
    };

    std::vector<int> lms_map(n + 1, -1);
    int m = 0;
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms_map[i] = m++;
        }
    }
    std::vector<int> lms;
    lms.reserve(m);
    for (int i = 1; i < n; i++) {
        if (!ls[i - 1] && ls[i]) {
            lms.push_back(i);
        }
    }

    induce(lms);

    if (m) {
        std::vector<int> sorted_lms;
        sorted_lms.reserve(m);
        for (int v : sa) {
            if (lms_map[v] != -1) sorted_lms.push_back(v);
        }
        std::vector<int> rec_s(m);
        int rec_upper = 0;
        rec_s[lms_map[sorted_lms[0]]] = 0;
        for (int i = 1; i < m; i++) {
            int l = sorted_lms[i - 1], r = sorted_lms[i];
            int end_l = (lms_map[l] + 1 < m) ? lms[lms_map[l] + 1] : n;
            int end_r = (lms_map[r] + 1 < m) ? lms[lms_map[r] + 1] : n;
            bool same = true;
            if (end_l - l != end_r - r) {
                same = false;
            } else {
                while (l < end_l) {
                    if (s[l] != s[r]) {
                        break;
                    }
                    l++;
                    r++;
                }
                if (l == n || s[l] != s[r]) same = false;
            }
            if (!same) rec_upper++;
            rec_s[lms_map[sorted_lms[i]]] = rec_upper;
        }

        auto rec_sa =
            sa_is<THRESHOLD_NAIVE, THRESHOLD_DOUBLING>(rec_s, rec_upper);

        for (int i = 0; i < m; i++) {
            sorted_lms[i] = lms[rec_sa[i]];
        }
        induce(sorted_lms);
    }
    return sa;
}

}  // namespace internal

std::vector<int> suffix_array(const std::vector<int>& s, int upper) {
    assert(0 <= upper);
    for (int d : s) {
        assert(0 <= d && d <= upper);
    }
    auto sa = internal::sa_is(s, upper);
    return sa;
}

template <class T> std::vector<int> suffix_array(const std::vector<T>& s) {
    int n = int(s.size());
    std::vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int l, int r) { return s[l] < s[r]; });
    std::vector<int> s2(n);
    int now = 0;
    for (int i = 0; i < n; i++) {
        if (i && s[idx[i - 1]] != s[idx[i]]) now++;
        s2[idx[i]] = now;
    }
    return internal::sa_is(s2, now);
}

std::vector<int> suffix_array(const std::string& s) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return internal::sa_is(s2, 255);
}

// Reference:
// T. Kasai, G. Lee, H. Arimura, S. Arikawa, and K. Park,
// Linear-Time Longest-Common-Prefix Computation in Suffix Arrays and Its
// Applications
template <class T>
std::vector<int> lcp_array(const std::vector<T>& s,
                           const std::vector<int>& sa) {
    int n = int(s.size());
    assert(n >= 1);
    std::vector<int> rnk(n);
    for (int i = 0; i < n; i++) {
        rnk[sa[i]] = i;
    }
    std::vector<int> lcp(n - 1);
    int h = 0;
    for (int i = 0; i < n; i++) {
        if (h > 0) h--;
        if (rnk[i] == 0) continue;
        int j = sa[rnk[i] - 1];
        for (; j + h < n && i + h < n; h++) {
            if (s[j + h] != s[i + h]) break;
        }
        lcp[rnk[i] - 1] = h;
    }
    return lcp;
}

std::vector<int> lcp_array(const std::string& s, const std::vector<int>& sa) {
    int n = int(s.size());
    std::vector<int> s2(n);
    for (int i = 0; i < n; i++) {
        s2[i] = s[i];
    }
    return lcp_array(s2, sa);
}

}  // namespace noya2
#line 4 "c.cpp"

#line 2 "/Users/noya2/Desktop/Noya2_library/data_structure/segment_tree.hpp"

namespace noya2{

template <class S, S (*op)(S, S), S (*e)()> struct segtree {
  public:
    segtree() : segtree(0) {}
    segtree(int n) : segtree(std::vector<S>(n, e())) {}
    segtree(const std::vector<S>& v) : _n(int(v.size())) {
        log = 0;
        size = 1;
        while (size < _n) size <<= 1, log++;

        d = std::vector<S>(2 * size, e());
        for (int i = 0; i < _n; i++) d[size + i] = v[i];
        for (int i = size - 1; i >= 1; i--) {
            update(i);
        }
    }

    void set(int p, S x) {
        assert(0 <= p && p < _n);
        p += size;
        d[p] = x;
        for (int i = 1; i <= log; i++) update(p >> i);
    }

    S get(int p) {
        assert(0 <= p && p < _n);
        return d[p + size];
    }

    S prod(int l, int r) {
        assert(0 <= l && l <= r && r <= _n);
        S sml = e(), smr = e();
        l += size;
        r += size;

        while (l < r) {
            if (l & 1) sml = op(sml, d[l++]);
            if (r & 1) smr = op(d[--r], smr);
            l >>= 1;
            r >>= 1;
        }
        return op(sml, smr);
    }

    S all_prod() { return d[1]; }

    template <bool (*f)(S)> int max_right(int l) {
        return max_right(l, [](S x) { return f(x); });
    }
    template <class F> int max_right(int l, F f) {
        assert(0 <= l && l <= _n);
        assert(f(e()));
        if (l == _n) return _n;
        l += size;
        S sm = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!f(op(sm, d[l]))) {
                while (l < size) {
                    l = (2 * l);
                    if (f(op(sm, d[l]))) {
                        sm = op(sm, d[l]);
                        l++;
                    }
                }
                return l - size;
            }
            sm = op(sm, d[l]);
            l++;
        } while ((l & -l) != l);
        return _n;
    }

    template <bool (*f)(S)> int min_left(int r) {
        return min_left(r, [](S x) { return f(x); });
    }
    template <class F> int min_left(int r, F f) {
        assert(0 <= r && r <= _n);
        assert(f(e()));
        if (r == 0) return 0;
        r += size;
        S sm = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!f(op(d[r], sm))) {
                while (r < size) {
                    r = (2 * r + 1);
                    if (f(op(d[r], sm))) {
                        sm = op(d[r], sm);
                        r--;
                    }
                }
                return r + 1 - size;
            }
            sm = op(d[r], sm);
        } while ((r & -r) != r);
        return 0;
    }

  private:
    int _n, size, log;
    std::vector<S> d;

    void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); }
};

} // namespace noya2
#line 6 "c.cpp"

int op(int a, int b){
    return min(a,b);
}
int e(){
    return iinf;
}

void solve(){
    int n, q; in(n,q);
    string s; in(s);
    reverse(all(s));
    auto sa = suffix_array(s);
    auto lcp = lcp_array(s,sa);
    segtree<int,op,e> seg(lcp);
    vector<int> inv(n);
    rep(i,n){
        inv[sa[i]] = i;
    }
    vector<pii> lrs(q);
    rep(i,q){
        int l, r; in(l,r); l--;
        lrs[i] = {n-r,n-l};
    }
    // min i
    // lcp(l,i) >= p
    // i >= l + p + 1
    auto nxt = [&](int l, int p){
        assert(l < n);
        int le = seg.min_left(inv[l], [&](int x){
            return x >= p;
        });
        int ri = seg.max_right(inv[l], [&](int x){
            return x >= p;
        });
        return tuple(le,ri+1,l+p+1);
    };
    vector<int> ls(q,0), rs(q,n+1);
    rep(tt,19){
        vector<int> ils(q);
        rep(i,q){
            ils[i] = lrs[i].first;
        }
        vector<int> ms(q);
        rep(i,q){
            ms[i] = (ls[i] + rs[i]) / 2;
        }
        rep(t,2){
            vector<tuple<int,int,int>> query(q);
            rep(i,q){
                if (ils[i] >= n) continue;
                query[i] = nxt(ils[i],ms[i]);
            }
            vector<vector<int>> qids(n);
            rep(i,q){
                auto [l, r, lb] = query[i];
                if (lb < n){
                    qids[lb].emplace_back(i);
                }
                else {
                    ils[i] = n+1;
                }
            }
            segtree<int,op,e> idsmin(n);
            reb(x,n){
                idsmin.set(inv[x],x);
                for (int i : qids[x]){
                    auto [l, r, lb] = query[i];
                    ils[i] = idsmin.prod(l,r);
                }
            }
        }
        rep(i,q){
            if (ils[i] + ms[i] + 1 <= lrs[i].second){
                ls[i] = ms[i];
            }
            else {
                rs[i] = ms[i];
            }
        }
    }
    rep(i,q){
        out(ls[i]);
    }
}

int main(){
    int t = 1; //in(t);
    while (t--) { solve(); }
}
accuracy: 1
time_ms: 1190
memory_kb: 26772
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
using ll=long long;
const ll ILL=2167167167167167167;
const int INF=2100000000;
#define rep(i,a,b) for (int i=(int)(a);i<(int)(b);i++)
#define all(p) p.begin(),p.end()
template<class T> using _pq = priority_queue<T, vector<T>, greater<T>>;
template<class T> int LB(vector<T> &v,T a){return lower_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> int UB(vector<T> &v,T a){return upper_bound(v.begin(),v.end(),a)-v.begin();}
template<class T> bool chmin(T &a,T b){if(b<a){a=b;return 1;}else return 0;}
template<class T> bool chmax(T &a,T b){if(a<b){a=b;return 1;}else return 0;}
template<class T> void So(vector<T> &v) {sort(v.begin(),v.end());}
template<class T> void Sore(vector<T> &v) {sort(v.begin(),v.end(),[](T x,T y){return x>y;});}
bool yneos(bool a,bool upp=false){if(a){cout<<(upp?"YES\n":"Yes\n");}else{cout<<(upp?"NO\n":"No\n");}return a;}
template<class T> void vec_out(vector<T> &p,int ty=0){
    if(ty==2){cout<<'{';for(int i=0;i<(int)p.size();i++){if(i){cout<<",";}cout<<'"'<<p[i]<<'"';}cout<<"}\n";}
    else{if(ty==1){cout<<p.size()<<"\n";}for(int i=0;i<(int)(p.size());i++){if(i) cout<<" ";cout<<p[i];}cout<<"\n";}}
template<class T> T vec_min(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmin(ans,x);return ans;}
template<class T> T vec_max(vector<T> &a){assert(!a.empty());T ans=a[0];for(auto &x:a) chmax(ans,x);return ans;}
template<class T> T vec_sum(vector<T> &a){T ans=T(0);for(auto &x:a) ans+=x;return ans;}
int pop_count(long long a){int res=0;while(a){res+=(a&1),a>>=1;}return res;}
template<class T> T square(T a){return a * a;}

#include <atcoder/string>

namespace po167{
    struct UF
    {
        using _F = int;
        int _n;
        std::vector<_F> wei;
        std::vector<int> q;
        int component;
        UF(int n):_n(n), wei(n), component(n), par(n){
            for (int i = 0; i < n; i++){
                wei[i] =1, par[i] = i;
            }
        }
        void intialize(){
            for (auto x : q){
                wei[root(x)] = 1;
                par[x] = x;
                wei[x] = 1;
            }
            component = (int)par.size();
            q = {};
        }
        //根っこを返す
        int root(int a){
            assert(0 <= a && a < _n);
            if (a == par[a]) return a;
            return par[a] = root(par[a]);
        }
        //trueなら1,falseなら0
        int same(int a, int b){
            assert(0 <= a && a < _n);
            assert(0 <= b && b < _n);
            if(root(a) == root(b)) return 1;
            else return 0;
        }
        _F size(int a){
            return wei[root(a)];
        }
        //a,bが違う根っこの元なら結合する,結合したらtrueを返す
        bool unite(int a,int b){
            a = root(a), b = root(b);
            if (a == b) return false;
            if (wei[a] < wei[b]) std::swap(a, b);
            par[b] = a;
            q.push_back(b);
            wei[a] += wei[b];
            component--;
            return true;
        }
    private:
        std::vector<int> par;
    };
}
using po167::UF;


void solve();
// POP'N ROLL MUSIC / TOMOO
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int t = 1;
    // cin >> t;
    rep(i, 0, t) solve();
}

void solve(){
    int N, Q;
    cin >> N >> Q;
    string S;
    cin >> S;
    reverse(all(S));
    vector<int> L(Q), R(Q);
    rep(i, 0, Q){
        cin >> L[i] >> R[i];
        L[i]--;
        swap(L[i], R[i]);
        L[i] = N - L[i];
        R[i] = N - R[i];
    }
    auto sa = atcoder::suffix_array(S);
    auto lcp = atcoder::lcp_array(S, sa);
    vector<int> inv(N);
    rep(i, 0, N) inv[sa[i]] = i;
    vector<int> order(N - 1);
    rep(i, 0, N - 1) order[i] = i;
    sort(all(order), [&](int l, int r){
        return lcp[l] > lcp[r];
    });
    vector<int> ansL(Q, 0), ansR(Q, N / 3);
    while (true){
        bool ok = false;
        vector<set<pair<int, int>>> s(N);
        vector<set<int>> val(N);
        rep(i, 0, N) val[i].insert(sa[i]);
        rep(i, 0, Q){
            if (ansR[i] - ansL[i] > 1){
                s[inv[L[i]]].insert({(ansR[i] + ansL[i]) / 2, i});
                ok = true;
            }
        }
        if (!ok) break;
        UF T(N);
        auto f = [&](int ind, int len) -> void {
            while (!s[ind].empty()){
                auto tmp = (*s[ind].rbegin());
                if (tmp.first <= len) break;
                auto [m, i] = tmp;
                /*cout << m << " " << i << endl;
                for (auto x : val[ind]) cout << x << " ";
                cout << endl;*/
                int l = L[i];
                l += m + 1;
                rep(rp, 0, 2){
                    if ((*val[ind].rbegin()) < l){
                        l = INF;
                        break;
                    }
                    l = (*val[ind].lower_bound(l));
                    l += m + 1;
                }
                if (R[i] < l) ansR[i] = m;
                else ansL[i] = m;
                s[ind].erase(tmp);
            }
        };
        for (auto x : order){
            int a = T.root(x);
            int b = T.root(x + 1);
            f(a, lcp[x]);
            f(b, lcp[x]);
            T.unite(a, b);
            if (T.root(a) == b) swap(a, b);
            if (s[a].size() < s[b].size()) swap(s[a], s[b]);
            for (auto y : s[b]) s[a].insert(y);
            if (val[a].size() < val[b].size()) swap(val[a], val[b]);
            for (auto y : val[b]) val[a].insert(y);
        }
        f(T.root(0), 0);
    }
    rep(i, 0, Q) cout << ansL[i] << "\n";
}
accuracy: 1
time_ms: 3020
memory_kb: 107036
final_rank: 9

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}


struct SuffixArray{
  string s;
  vector<int> sa,rev;

  SuffixArray(){}
  SuffixArray(const string &S):s(S){
    int n=s.size();
    s.push_back('$');
    sa.resize(n+1);
    iota(sa.begin(),sa.end(),0);
    sort(sa.begin(),sa.end(),
         [&](int a,int b){
           if(s[a]==s[b]) return a>b;
           return s[a]<s[b];
         });
    vector<int> c(n+1,0),r(n+1),cnt(n+1);
    for(int i=0;i<=n;i++) r[i]=s[i];
    for(int len=1;len<=n;len*=2){
      for(int i=0;i<=n;i++){
        c[sa[i]]=i;
        if(i>0 &&
           r[sa[i-1]]==r[sa[i]] &&
           sa[i-1]+len<=n &&
           r[sa[i-1]+len/2]==r[sa[i]+len/2]) c[sa[i]]=c[sa[i-1]];
      }
      iota(cnt.begin(),cnt.end(),0);
      copy(sa.begin(),sa.end(),r.begin());
      for(int i=0;i<=n;i++){
        int s1=r[i]-len;
        if(s1>=0) sa[cnt[c[s1]]++]=s1;
      }
      c.swap(r);
    }
    rev.resize(n+1);
    for(int i=0;i<=n;i++) rev[sa[i]]=i;
  }
  int operator[](int i) const{return sa[i];}

  bool lt_substr(string &t,int si,int ti){
    int sn=s.size(),tn=t.size();
    while(si<sn&&ti<tn){
      if(s[si]<t[ti]) return 1;
      if(s[si]>t[ti]) return 0;
      si++;ti++;
    }
    return si==sn&&ti<tn;
  }

  int lower_bound(string& t){
    int l=0,r=s.size();
    while(l+1<r){
      int m=(l+r)>>1;
      if(lt_substr(t,sa[m],0)) l=m;
      else r=m;
    }
    return r;
  }

  int upper_bound(string& t){
    t.back()++;
    int res=lower_bound(t);
    t.back()--;
    return res;
  }

  // O(|T|*log|S|)
  int count(string& T){
    return upper_bound(T)-lower_bound(T);
  }
};


struct LongestCommonPrefix{
  SuffixArray sa;

  vector<int> ht;
  vector< vector<int> > dat;
  LongestCommonPrefix(string &s):sa(s){
    int n=s.size();
    vector<int> lcp(n,0);

    int t=0;
    lcp[0]=0;
    for(int i=0;i<n;i++){
      int j=sa[sa.rev[i]-1];
      if(t>0) t--;
      for(;j+t<n&&i+t<n;t++){
        if(sa.s[j+t]!=sa.s[i+t]) break;
      }
      lcp[sa.rev[i]-1]=t;
    }

    int h=1;
    while((1<<h)<n) h++;
    dat.assign(h,vector<int>(n));
    ht.assign(n+1,0);
    for(int j=2;j<=n;j++) ht[j]=ht[j>>1]+1;

    for(int j=0;j<n;j++) dat[0][j]=lcp[j];
    for(int i=1,p=1;i<h;i++,p<<=1)
      for(int j=0;j<n;j++)
        dat[i][j]=min(dat[i-1][j],dat[i-1][min(j+p,n-1)]);
  }

  // a, b are indices for suffix array
  int query(int a,int b){
    assert(a!=b);
    if(a>b) swap(a,b);
    int l=b-a;
    return min(dat[ht[l]][a],dat[ht[l]][b-(1<<ht[l])]);
  }

  // a, b are indices for string
  int lcp(int a,int b){
    return query(sa.rev[a],sa.rev[b]);
  }
};


struct FullyIndexableDictionary{
  int len,blk;
  vector<unsigned> bit;
  vector<int> sum;

  FullyIndexableDictionary(){}
  FullyIndexableDictionary(int len)
    :len(len),blk((len+31)>>5),bit(blk,0),sum(blk,0){}

  void set(int k){
    bit[k>>5]|=1u<<(k&31);
  }

  void build(){
    sum[0]=0;
    for(int i=1;i<blk;i++)
      sum[i]=sum[i-1]+__builtin_popcount(bit[i-1]);
  }

  bool operator[](int k) const{
    return bool((bit[k>>5]>>(k&31))&1);
  }

  int rank(int k){
    return sum[k>>5]+__builtin_popcount(bit[k>>5]&((1u<<(k&31))-1));
  }

  int rank(bool v,int k){
    return (v?rank(k):k-rank(k));
  }

  int select(bool v,int k){
    if(k<0||rank(v,len)<=k) return -1;
    int l=0,r=len;
    while(l+1<r){
      int m=(l+r)>>1;
      if(rank(v,m)>=k+1) r=m;
      else l=m;
    }
    return r-1;
  }

  int select(bool v,int i,int l){
    return select(v,i+rank(v,l));
  }
};

template<class T,int MAXLOG>
struct WaveletMatrix{
  int len;
  FullyIndexableDictionary mat[MAXLOG];
  int zs[MAXLOG],buff1[MAXLOG],buff2[MAXLOG];
  static const T npos=-1;

  int freq_dfs(int d,int l,int r,T val,T a,T b){
    if(l==r) return 0;
    if(d==MAXLOG) return (a<=val&&val<b)?r-l:0;
    T nv=T(1)<<(MAXLOG-d-1)|val;
    T nnv=((T(1)<<(MAXLOG-d-1))-1)|nv;
    if(nnv<a||b<=val) return 0;
    if(a<=val&&nnv<b) return r-l;
    int lc=mat[d].rank(1,l),rc=mat[d].rank(1,r);
    return freq_dfs(d+1,l-lc,r-rc,val,a,b)
      +freq_dfs(d+1,lc+zs[d],rc+zs[d],nv,a,b);
  }

  WaveletMatrix(vector<T> data){
    len=data.size();
    vector<T> l(len),r(len);
    for(int dep=0;dep<MAXLOG;dep++){
      mat[dep]=FullyIndexableDictionary(len+1);
      int p=0,q=0;
      for(int i=0;i<len;i++){
        bool k=(data[i]>>(MAXLOG-(dep+1)))&1;
        if(k) r[q++]=data[i],mat[dep].set(i);
        else  l[p++]=data[i];
      }
      zs[dep]=p;
      mat[dep].build();
      swap(l,data);
      for(int i=0;i<q;i++) data[p+i]=r[i];
    }
  }

  T access(int k){
    T res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      bool bit=mat[dep][k];
      res=(res<<1)|bit;
      k=mat[dep].rank(bit,k)+zs[dep]*dep;
    }
    return res;
  }

  // return the number of v in [0,k)
  int rank(T v,int k){
    int l=0,r=k;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return r-l;
  }

  // return the position of k-th v
  int select(T v,int k){
    rank(v,len);
    for(int dep=MAXLOG-1;dep>=0;dep--){
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      k=mat[dep].select(bit,k,buff1[dep]);
      if(k>=buff2[dep]||k<0) return -1;
      k-=buff1[dep];
    }
    return k;
  }

  int select(T v,int k,int l){
    return select(v,k+rank(v,l));
  }

  // return k-th largest value in [l,r)
  T quantile(int l,int r,int k){
    if(r-l<=k||k<0) return -1;
    T res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      int p=mat[dep].rank(1,l);
      int q=mat[dep].rank(1,r);
      if(q-p>k){
        l=p+zs[dep];
        r=q+zs[dep];
        res|=T(1)<<(MAXLOG-(dep+1));
      }else{
        k-=(q-p);
        l-=p;
        r-=q;
      }
    }
    return res;
  }

  T rquantile(int l,int r,int k){
    return quantile(l,r,r-l-k-1);
  }

  // return number of points in [left, right) * [lower, upper)
  int rangefreq(int left,int right,T lower,T upper){
    return freq_dfs(0,left,right,0,lower,upper);
  }

  pair<int, int> ll(int l,int r,T v){
    int res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      if(bit) res+=r-l+mat[dep].rank(bit,l)-mat[dep].rank(bit,r);
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return make_pair(res,r-l);
  }

  int lt(int l,int r,T v){
    auto p=ll(l,r,v);
    return p.first;
  }

  int le(int l,int r,T v){
    auto p=ll(l,r,v);
    return p.first+p.second;
  }

  T succ(int l,int r,T v){
    int k=le(l,r,v);
    return k==r-l?npos:rquantile(l,r,k);
  }

  T pred(int l,int r,T v){
    int k=lt(l,r,v);
    return k?rquantile(l,r,k-1):npos;
  }
};

//INSERT ABOVE HERE

signed main(){
  cin.tie(0);
  ios::sync_with_stdio(0);

  int n,q;
  cin>>n>>q;
  string s;
  cin>>s;

  LongestCommonPrefix lcp(s);
  auto vs=lcp.sa.sa;
  auto rev=lcp.sa.rev;
  using WM = WaveletMatrix<int, 18>;
  WM wm(vs);

  auto calc=
    [&](int a,int b)->int{
      auto check=
        [&](int x)->int{
          int p=b-x;
          int pos=rev[p];
          int ll=-1,rr=-1;
          {
            int l=-1,r=pos;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&lcp.lcp(vs[m],p)>=x) r=m;
              else l=m;
            }
            ll=r;
          }
          {
            int l=pos,r=n+1;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&lcp.lcp(vs[m],p)>=x) l=m;
              else r=m;
            }
            rr=r;
          }
          // [ll, rr)
          int q=wm.pred(ll,rr,p-x);
          if(q==WM::npos||q-x<0) return 0;
          int k=wm.pred(ll,rr,q-x);
          if(k==WM::npos) return 0;
          return a<k;
        };

      int l=0,r=(b-a+2)/3;
      while(l+1<r){
        int m=(l+r)>>1;
        if(check(m)) l=m;
        else r=m;
      }
      return l;
    };

  for(int i=0;i<q;i++){
    int a,b;
    cin>>a>>b;
    a--;
    cout<<calc(a,b)<<"\n";
  }
  cout<<flush;
  return 0;
}
accuracy: 1
time_ms: 3760
memory_kb: 24388
final_rank: 7

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

struct FastIO{
  FastIO(){
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
}fastio_beet;


struct SuffixArray{
  int n;
  string S;
  vector<int> sa,lcp,rev;
  SuffixArray(){}
  SuffixArray(string &S):S(S){init();}
  void init(){
    n=S.length();
    S.push_back('$');
    build_sa();
  }
  void build_sa(){
    sa.assign(n+1,0);
    iota(sa.begin(),sa.end(),0);
    sort(sa.begin(),sa.end(),
         [&](int a,int b){
           if(S[a]==S[b]) return a>b;
           return S[a]<S[b];
         });
    vector<int> c(n+1,0),r(n+1),cnt(n+1),s(n+1);
    for(int i=0;i<=n;i++) r[i]=S[i];
    for(int len=1;len<=n;len*=2){
      for(int i=0;i<=n;i++){
        c[sa[i]]=
          i>0 &&
          r[sa[i-1]]==r[sa[i]] &&
          sa[i-1]+len<=n &&
          r[sa[i-1]+len/2]==r[sa[i]+len/2] ?
          c[sa[i-1]]:i;
      }
      iota(cnt.begin(),cnt.end(),0);
      copy(sa.begin(),sa.end(),r.begin());
      for(int i=0;i<=n;i++){
        int s1=r[i]-len;
        if(s1>=0) sa[cnt[c[s1]]++]=s1;
      }
      c.swap(r);
    }
  }
};


struct RollingHash{
  using ull = unsigned long long;
  vector<ull> hash,p;
  RollingHash(){}
  RollingHash(const string &s,ull B=1000000007LL){
    int n=s.size();
    hash.assign(n+1,0);
    p.assign(n+1,1);
    for(int i=0;i<n;i++){
      hash[i+1]=hash[i]*B+s[i];
      p[i+1]=p[i]*B;
    }
  }
  //S[l, r)
  ull find(int l,int r){
    return hash[r]-hash[l]*p[r-l];
  }
};


struct FullyIndexableDictionary{
  int len,blk;
  vector<unsigned> bit;
  vector<int> sum;
  
  FullyIndexableDictionary(){}
  FullyIndexableDictionary(int len)
    :len(len),blk((len+31)>>5),bit(blk,0),sum(blk,0){}
  
  void set(int k){
    bit[k>>5]|=1u<<(k&31);
  }

  void build(){
    sum[0]=0;
    for(int i=1;i<blk;i++)
      sum[i]=sum[i-1]+__builtin_popcount(bit[i-1]);
  }

  bool operator[](int k) const{
    return bool((bit[k>>5]>>(k&31))&1);
  }
  
  int rank(int k){
    return sum[k>>5]+__builtin_popcount(bit[k>>5]&((1u<<(k&31))-1));
  }
  
  int rank(bool v,int k){
    return (v?rank(k):k-rank(k));
  }

  int select(bool v,int k){
    if(k<0||rank(v,len)<=k) return -1;
    int low=0,high=len;
    while(low+1<high){
      int mid=(low+high)>>1;
      if(rank(v,mid)>=k+1) high=mid;
      else low=mid;
    }
    return high-1;
  }

  int select(bool v,int i,int l){
    return select(v,i+rank(v,l));
  }
};

template<class T,int MAXLOG>
struct WaveletMatrix{
  int len;
  FullyIndexableDictionary mat[MAXLOG];
  int zs[MAXLOG],buff1[MAXLOG],buff2[MAXLOG];
  static const T npos=-1;
  
  WaveletMatrix(vector<T> data){
    len=data.size();
    vector<T> l(len),r(len);
    for(int dep=0;dep<MAXLOG;dep++){
      mat[dep]=FullyIndexableDictionary(len+1);
      int p=0,q=0;
      for(int i=0;i<len;i++){
        bool k=(data[i]>>(MAXLOG-(dep+1)))&1;
        if(k) r[q++]=data[i],mat[dep].set(i);
        else  l[p++]=data[i];
      }
      zs[dep]=p;
      mat[dep].build();
      swap(l,data);
      for(int i=0;i<q;i++) data[p+i]=r[i];
    }
  }
  
  T access(int k){
    T res=0;
    bool bit;
    for(int dep=0;dep<MAXLOG;dep++){
      bit=mat[dep][k];
      res=(res<<1)|bit;
      k=mat[dep].rank(bit,k)+zs[dep]*dep;
    }
    return res;
  }

  // return the number of v in [0,k)
  int rank(T v,int k){
    int l=0,r=k;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return r-l;
  }

  // return the position of k-th v
  int select(T v,int k){
    rank(v,len);
    for(int dep=MAXLOG-1;dep>=0;dep--){
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      k=mat[dep].select(bit,k,buff1[dep]);
      if(k>=buff2[dep]||k<0) return -1;
      k-=buff1[dep];
    }
    return k;
  }

  int select(T v,int k,int l){
    return select(v,k+rank(v,l));
  }

  // return k-th largest value in [l,r)
  T quantile(int l,int r,int k){
    if(r-l<=k||k<0) return -1;
    T res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      int p=mat[dep].rank(1,l);
      int q=mat[dep].rank(1,r);
      if(q-p>k){
        l=p+zs[dep];
        r=q+zs[dep];
        res|=(1<<(MAXLOG-(dep+1)));
      }else{
        k-=(q-p);
        l-=p;
        r-=q;
      }
    }
    return res;
  }
  
  T rquantile(int l,int r,int k){
    return quantile(l,r,r-l-k-1);
  }
  
  pair<int, int> ll(int l,int r,T v){
    int res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      if(bit) res+=r-l+mat[dep].rank(bit,l)-mat[dep].rank(bit,r);
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return make_pair(res,r-l);    
  }
  
  int lt(int l,int r,T v){
    auto p=ll(l,r,v);
    return p.first;
  }
  
  int le(int l,int r,T v){
    auto p=ll(l,r,v);
    return p.first+p.second;
  }
  
  T succ(int l,int r,T v){
    int k=le(l,r,v);
    return k==r-l?npos:rquantile(l,r,k);
  }

  T pred(int l,int r,T v){
    int k=lt(l,r,v);
    return k?rquantile(l,r,k-1):npos;
  }
};


//INSERT ABOVE HERE
signed main(){
  int n,q;
  cin>>n>>q;
  string s;
  cin>>s;

  SuffixArray sa(s);
  vector<int> rev(n);
  auto vs=sa.sa;
  vs.erase(vs.begin());
  for(int i=0;i<n;i++){
    rev[vs[i]]=i;
  }
  using WM = WaveletMatrix<int, 18>;
  WM wm(vs);

  RollingHash rh(s);
  auto calc=
    [&](int a,int b)->int{
      auto check=
        [&](int x)->int{
          int p=b-x;
          int pos=rev[p];
          int ll=-1,rr=-1;
          {
            int l=-1,r=pos;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&rh.find(vs[m],vs[m]+x)==rh.find(p,p+x)) r=m;
              else l=m;
            }
            ll=r;
          }
          {
            int l=pos,r=n;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&rh.find(vs[m],vs[m]+x)==rh.find(p,p+x)) l=m;
              else r=m;
            }
            rr=r;
          }
          // [ll, rr)
          int q=wm.pred(ll,rr,p-x);
          if(q==WM::npos||q-x<0) return 0;
          int k=wm.pred(ll,rr,q-x);
          if(k==WM::npos) return 0;
          return a<k;
        };
      
      int l=0,r=(b-a+2)/3;
      while(l+1<r){
        int m=(l+r)>>1;
        if(check(m)) l=m;
        else r=m;
      }
      return l;
    };
    
  for(int i=0;i<q;i++){
    int a,b;
    cin>>a>>b;
    a--;
    cout<<calc(a,b)<<"\n";
  }
  cout<<flush;
  return 0;
}
accuracy: 1
time_ms: 3020
memory_kb: 10376
final_rank: 4

code_snippet: #include <bits/stdc++.h>
#define ll long long
#define INF 1000000005
#define MOD 1000000007
#define EPS 1e-10
#define rep(i,n) for(int i=0;i<(int)(n);++i)
#define rrep(i,n) for(int i=(int)(n)-1;i>=0;--i)
#define srep(i,s,t) for(int i=(int)(s);i<(int)(t);++i)
#define each(a,b) for(auto& (a): (b))
#define all(v) (v).begin(),(v).end()
#define len(v) (int)(v).size()
#define zip(v) sort(all(v)),v.erase(unique(all(v)),v.end())
#define cmx(x,y) x=max(x,y)
#define cmn(x,y) x=min(x,y)
#define fi first
#define se second
#define pb push_back
#define show(x) cout<<#x<<" = "<<(x)<<endl
#define sar(a,n) cout<<#a<<":";rep(pachico,n)cout<<" "<<a[pachico];cout<<endl
#pragma GCC optimize("O3")
#pragma GCC target("avx")

using namespace std;

template<typename S,typename T>auto&operator<<(auto&o,pair<S,T>p){return o<<"{"<<p.fi<<","<<p.se<<"}";}
template<typename T>auto&operator<<(auto&o,set<T>s){for(auto&e:s)o<<e<<" ";return o;}
template<typename S,typename T,typename U>
auto&operator<<(auto&o,priority_queue<S,T,U>q){while(!q.empty())o<<q.top()<<" ",q.pop();return o;}
template<typename K,typename T>auto&operator<<(auto&o,map<K,T>&m){for(auto&e:m)o<<e<<" ";return o;}
template<typename T>auto&operator<<(auto&o,vector<T>v){for(auto&e:v)o<<e<<" ";return o;}
void ashow(){cout<<endl;}template<typename T,typename...A>void ashow(T t,A...a){cout<<t<<" ";ashow(a...);}
template<typename S,typename T,typename U>
struct TRI{S fi;T se;U th;TRI(){}TRI(S f,T s,U t):fi(f),se(s),th(t){}};
template<typename S,typename T,typename U>
auto& operator<<(auto& o,TRI<S,T,U>& t){return o<<"{"<<t.fi<<","<<t.se<<","<<t.th<<"}";}

typedef pair<int, int> P;
typedef pair<ll, ll> pll;
typedef TRI<int, int, int> tri;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<vi> vvi;
typedef vector<vl> vvl;
typedef vector<P> vp;
typedef vector<double> vd;
typedef vector<string> vs;

const int MAX_N = 200005;

class SA_IS
{
private:
    using byte = unsigned char;
    byte mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
    #define tget(i) !!(t[(i)>>3]&mask[(i)&7])
    #define tset(i, b) t[(i)>>3]=(b) ? (mask[(i)&7]|t[(i)>>3]) : ((~mask[(i)&7])&t[(i)>>3])
    #define chr(i) (cs==sizeof(int)?((int*)s)[i]:((byte *)s)[i])
    #define isLMS(i) (i>0 && tget(i) && !tget(i-1))
    void getBuckets(byte *s, int *bkt, int n, int K, int cs, bool end=true){
        fill(bkt, bkt + K + 1, 0);
        for(int i = 0; i < n; i++){
            bkt[chr(i)]++;
        }
        for(int i = 0,tmp = 0; i < K+1; i++){
            tmp += bkt[i];
            bkt[i] = end ? tmp : tmp - bkt[i];
        }
    }
    void induceSAl(byte *t, byte *s, int *bkt, int n, int K, int cs){
        getBuckets(s, bkt, n, K, cs, false);
        for(int i = 0; i < n; i++){
            if(sa[i]>0 && !tget(sa[i]-1)){
                sa[bkt[chr(sa[i]-1)]++] = sa[i]-1;
            }
        }
    }
    void induceSAs(byte *t, byte *s, int *bkt, int n, int K, int cs){
        getBuckets(s, bkt, n, K, cs, true);
        for(int i = n-1; i >= 0; i--){
            if(sa[i] > 0 && tget(sa[i]-1)){
                sa[--bkt[chr(sa[i]-1)]] = sa[i]-1;
            }
        }
    }
    void make_sa(byte *s, int n, int K=128, int cs=1){
        byte t[(n >> 3)+1];
        int bkt[K+1], n1 = 0, name = 0;
        tset(n-2, 0), tset(n-1, 1);
        for(int i = n - 3; i >=0; i--){
            tset(i, (chr(i)<chr(i+1) || (chr(i)==chr(i+1) && tget(i+1))));
        }
        getBuckets(s, bkt, n, K, cs);
        fill(sa, sa+n, -1);
        for(int i = 1; i < n; i++){
            if(isLMS(i)){
                sa[--bkt[chr(i)]] = i;
            }
        }
        induceSAl(t, s, bkt, n, K, cs);
        induceSAs(t, s, bkt, n, K, cs);
        for(int i = 0; i < n; i++){
            if(isLMS(sa[i])){
                sa[n1++] = sa[i];
            }
        }
        fill(sa + n1, sa + n, -1);
        for(int i = 0, tmp = -1; i < n1; i++){
            int pos = sa[i], diff = false;
            for(int d = 0; d < n && !diff; d++){
                diff = chr(pos+d) != chr(tmp+d) || tget(pos+d) != tget(tmp+d);
                if(!diff && d && (isLMS(pos+d) || isLMS(tmp+d))) break;
            }
            if(diff){
                name++, tmp = pos;
            }
            sa[n1+((pos - (pos & 1)) >> 1)] = name - 1;
        }
        int* s1 = sa + n - n1;
        for(int i = n - 1,j = n - 1; i >= n1; i--){
            if(sa[i] >= 0){
                sa[j--] = sa[i];
            }
        }
        if(name < n1){
            make_sa((byte*)s1, n1, name - 1, sizeof(int));
        }else{
            for(int i = 0; i < n1; i++){
                sa[s1[i]] = i;
            }
        }
        getBuckets(s, bkt, n, K, cs);
        for(int i = 1, j = 0; i < n; i++){
            if(isLMS(i)){
                s1[j++] = i;
            }
        }
        for(int i = 0; i < n1; i++){
            sa[i] = s1[sa[i]];
        }
        fill(sa + n1, sa + n, -1);
        for(int i = n1 - 1; i >= 0; i--){
          int tmp = sa[i];
          sa[i] = -1, sa[--bkt[chr(tmp)]] = tmp;
        }
        induceSAl(t, s, bkt, n, K, cs);
        induceSAs(t, s, bkt, n, K, cs);
    }
    void make_lcp(){
        lcp = new int[sz+1];
        rnk = new int[sz+1];
        for(int i = 0; i <= sz; i++) rnk[sa[i]] = i;
        lcp[0] = 0;
        for(int i = 0, h = 0; i < sz; i++){
            int j = sa[rnk[i]-1];
            if(h > 0) h--;
            for(;j+h<sz&&i+h<sz;h++){
                if(CS[j+h] != CS[i+h]) break;
            }
            lcp[rnk[i]-1] = h;
        }
    }
public:
    bool contain(const string& T){
        int a = 0, b = sz;
        while(b - a > 1){
            int c = (a + b) / 2;
            if(CS.compare(sa[c], T.length(), T) < 0){
                a = c;
            }else{
                b = c;
            }
        }
        return CS.compare(sa[b], T.length(), T) == 0;
    }
    string CS;
    byte* S;
    int sz;
    int *sa, *lcp, *rnk;
    SA_IS(string& arg){
        CS = arg;
        sz = (int)arg.size();
        sa = new int[sz+1];
        S = (byte*)arg.c_str();
        make_sa(S, sz+1);
        make_lcp();
    }
    ~SA_IS(){
        delete[] sa;
        // delete[] lcp; delete[] rnk;
    }
};

#define MAX_BIT 64

struct BitRank {
    // block: bit 列を管理, count: block ごとに立っている 1 の数を管理
    vector<unsigned long long> block;
    vector<int> count;
    BitRank(){}
    void resize(int num) {
        block.resize((num + 1) / MAX_BIT + 1, 0);
        count.resize((int)block.size(), 0);
    }
    // i ビット目を val(0,1) にセット
    inline void set(int i, unsigned long long val) {
        block[i/MAX_BIT] |= (val << (i % MAX_BIT));
    }
    void build() {
        for(int i = 1; i < (int)block.size(); i++){
            count[i] = count[i-1] + __builtin_popcountll(block[i-1]);
        }
    }
    // [0, i) ビットの 1 の数
    inline int rank1(int i) {
        int j = i/MAX_BIT, k = i % MAX_BIT;
        return count[j] + (k ? __builtin_popcountll(block[j] << (MAX_BIT-k)) : 0);
    }
    // [i, j) ビットの 1 の数
    inline int rank1(int i, int j) {
        return rank1(j) - rank1(i);
    }
    // [0, i) ビットの 0 の数
    inline int rank0(int i) {
        return i - rank1(i);
    }
    // [i, j) ビットの 0 の数
    inline int rank0(int i, int j) {
        return rank0(j) - rank0(i);
    }
};

class WaveletMatrix
{
private:
    int height;
    vector<BitRank> B;
    vector<int> pos;
public:
    WaveletMatrix(){}
    WaveletMatrix(vector<int>& vec) :
        WaveletMatrix(vec, *max_element(vec.begin(), vec.end()) + 1) {}
    // sigma:文字の種類数
    WaveletMatrix(vector<int>& vec, int sigma){
        init(vec, sigma);
    }
    void init(vector<int>& vec, int sigma){
        height = (sigma == 1) ? 1 : (MAX_BIT - __builtin_clzll(sigma-1));
        B.resize(height), pos.resize(height);
        for(int i = 0; i < height; i++){
            B[i].resize((int)vec.size());
            for(int j = 0; j < (int)vec.size(); j++) {
                B[i].set(j, get(vec[j], height - i - 1));
            }
            B[i].build();
            auto it = stable_partition(vec.begin(), vec.end(), [&](int c) {
                return !get(c, height - i - 1);
            });
            pos[i] = it - vec.begin();
        }
    }
    // val の i ビット目の値を返す(0,1)
    inline int get(int val, int i) {
        return val >> i & 1;
    }
    // [l, r) の間に現れる値 val の数
    int rank(int val, int l, int r) {
        return rank(val, r) - rank(val, l);
    }
    // [0, i) の間に現れる値 val の数
    int rank(int val, int i) {
        int p = 0;
        for(int j = 0; j < height; j++){
            if(get(val, height - j - 1)){
                p = pos[j] + B[j].rank1(p);
                i = pos[j] + B[j].rank1(i);
            }else{
                p = B[j].rank0(p);
                i = B[j].rank0(i);
            }
        }
        return i - p;
    }
    // [l, r) の k(0,1,2...) 番目に小さい値を返す
    int quantile(int k, int l, int r) {
        int res = 0;
        for(int i = 0; i < height; i++){
            int j = B[i].rank0(l, r);
            if(j > k){
                l = B[i].rank0(l);
                r = B[i].rank0(r);
            }else{
                l = pos[i] + B[i].rank1(l);
                r = pos[i] + B[i].rank1(r);
                k -= j;
                res |= (1 << (height - i - 1));
            }
        }
        return res;
    }
    int rangefreq(int i, int j, int a, int b, int l, int r, int x) {
        if(i == j || r <= a || b <= l) return 0;
        int mid = (l + r) >> 1;
        if(a <= l && r <= b){
            return j - i;
        }else{
            int left = rangefreq(B[x].rank0(i),B[x].rank0(j),a,b,l,mid,x+1);
            int right = rangefreq(pos[x]+B[x].rank1(i),pos[x]+B[x].rank1(j),a,b,mid,r,x+1);
            return left + right;
        }
    }
    // [l,r) で値が [a,b) 内に含まれる数を返す
    int rangefreq(int l, int r, int a, int b) {
        return rangefreq(l, r, a, b, 0, 1 << height, 0);
    }
    int range(int i, int j, int a, int b, int l, int r, int x, int val) {
        if(i == j || r <= a || b <= l) return -1;
        if(r - l == 1) return val;
        int mid = (l + r) >> 1;
        int res = range(B[x].rank0(i),B[x].rank0(j),a,b,l,mid,x+1,val);
        if(res < 0) return range(pos[x]+B[x].rank1(i),pos[x]+B[x].rank1(j),a,b,mid,r,x+1,val+(1 << (height - x - 1)));
        else return res;
    }
    // [l,r) で値が [a,b) 内に最小の数を返す
    int range(int l, int r, int a, int b) {
        return range(l, r, a, b, 0, 1 << height, 0, 0);
    }
};

template<typename T> class SparseTable {
private:
    vector<int> LogTable;
    vector<vector<T> > Table; //最小値を保持
    int sz;
public:
    SparseTable(vector<T>& v){
        sz = (int)v.size();
        LogTable.resize(sz+1);
        for(int i = 2; i < sz + 1; i++){
            LogTable[i] = LogTable[i >> 1] + 1;
        }
        Table.resize(sz,vector<T>(LogTable[sz]+1));
        rep(i,sz){
            Table[i][0] = v[i];
        }
        for(int k = 1; (1 << k) <= sz; k++){
            for(int i = 0; i + (1 << k) <= sz; i++){
                Table[i][k] = min(Table[i][k-1],Table[i + (1 << (k-1))][k-1]);
            }
        }
    }
    T query(int l,int r){
        int k = LogTable[r-l];
        return min(Table[l][k],Table[r-(1<<k)][k]);
    }
};

int unzip[MAX_N];
int l, r, n, q;

bool possible(int cri, SA_IS& sa, WaveletMatrix& wm, SparseTable<int>& st)
{
    int left, right;
    if(unzip[r-cri] == 0 || st.query(unzip[r-cri]-1, unzip[r-cri]) < cri){
        left = unzip[r-cri];
    }else{
        int x = -1, y = unzip[r-cri]-1;
        while(y-x>1){
            int mid = (x+y)/2;
            if(st.query(mid, unzip[r-cri]) >= cri){
                y = mid;
            }else{
                x = mid;
            }
        }
        left = y;
    }
    if(unzip[r-cri] == n-1 || st.query(unzip[r-cri], unzip[r-cri]+1) < cri){
        right = unzip[r-cri];
    }else{
        int x = unzip[r-cri]+1, y = n;
        while(y-x>1){
            int mid = (x+y)/2;
            if(st.query(unzip[r-cri], mid) >= cri){
                x = mid;
            }else{
                y = mid;
            }
        }
        right = x;
    }
    // if(!wm.rangefreq(left, right+1, l+1, r-3*cri-1)) return false;
    int res = wm.range(left, right+1, l+1, r-3*cri-1);
    if(res < 0) return false;
    return wm.rangefreq(left, right+1, res+cri+1, r-2*cri);
}

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin >> n >> q;
    string s;
    cin >> s;
    SA_IS sa(s);
    vi vec(n), vec2(n-1);
    rep(i,n) vec[i] = sa.sa[i+1], unzip[sa.sa[i+1]] = i;
    rep(i,n-1) vec2[i] = sa.lcp[i+1];
    SparseTable<int> st(vec2);
    WaveletMatrix wm(vec);
    rep(i,q){
        cin >> l >> r;
        --l;
        if(r-l<6){
            cout << "0\n";
            continue;
        }
        int x = 0, y = (r-l-3)/3+1;
        while(y-x>1){
            int mid = (x+y)/2;
            if(possible(mid, sa, wm, st)){
                x = mid;
            }else{
                y = mid;
            }
        }
        cout << x << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 5130
memory_kb: 30424
final_rank: 8

code_snippet: #include <bits/stdc++.h>
#define ll long long
#define INF 1000000005
#define MOD 1000000007
#define EPS 1e-10
#define rep(i,n) for(int i=0;i<(int)(n);++i)
#define rrep(i,n) for(int i=(int)(n)-1;i>=0;--i)
#define srep(i,s,t) for(int i=(int)(s);i<(int)(t);++i)
#define each(a,b) for(auto& (a): (b))
#define all(v) (v).begin(),(v).end()
#define len(v) (int)(v).size()
#define zip(v) sort(all(v)),v.erase(unique(all(v)),v.end())
#define cmx(x,y) x=max(x,y)
#define cmn(x,y) x=min(x,y)
#define fi first
#define se second
#define pb push_back
#define show(x) cout<<#x<<" = "<<(x)<<endl
#define sar(a,n) cout<<#a<<":";rep(pachico,n)cout<<" "<<a[pachico];cout<<endl
#pragma GCC optimize("O3")
#pragma GCC target("avx")

using namespace std;

template<typename S,typename T>auto&operator<<(auto&o,pair<S,T>p){return o<<"{"<<p.fi<<","<<p.se<<"}";}
template<typename T>auto&operator<<(auto&o,set<T>s){for(auto&e:s)o<<e<<" ";return o;}
template<typename S,typename T,typename U>
auto&operator<<(auto&o,priority_queue<S,T,U>q){while(!q.empty())o<<q.top()<<" ",q.pop();return o;}
template<typename K,typename T>auto&operator<<(auto&o,map<K,T>&m){for(auto&e:m)o<<e<<" ";return o;}
template<typename T>auto&operator<<(auto&o,vector<T>v){for(auto&e:v)o<<e<<" ";return o;}
void ashow(){cout<<endl;}template<typename T,typename...A>void ashow(T t,A...a){cout<<t<<" ";ashow(a...);}
template<typename S,typename T,typename U>
struct TRI{S fi;T se;U th;TRI(){}TRI(S f,T s,U t):fi(f),se(s),th(t){}};
template<typename S,typename T,typename U>
auto& operator<<(auto& o,TRI<S,T,U>& t){return o<<"{"<<t.fi<<","<<t.se<<","<<t.th<<"}";}

typedef pair<int, int> P;
typedef pair<ll, ll> pll;
typedef TRI<int, int, int> tri;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<vi> vvi;
typedef vector<vl> vvl;
typedef vector<P> vp;
typedef vector<double> vd;
typedef vector<string> vs;

const int MAX_N = 200005;

class SA_IS
{
private:
    using byte = unsigned char;
    byte mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
    #define tget(i) !!(t[(i)>>3]&mask[(i)&7])
    #define tset(i, b) t[(i)>>3]=(b) ? (mask[(i)&7]|t[(i)>>3]) : ((~mask[(i)&7])&t[(i)>>3])
    #define chr(i) (cs==sizeof(int)?((int*)s)[i]:((byte *)s)[i])
    #define isLMS(i) (i>0 && tget(i) && !tget(i-1))
    void getBuckets(byte *s, int *bkt, int n, int K, int cs, bool end=true){
        fill(bkt, bkt + K + 1, 0);
        for(int i = 0; i < n; i++){
            bkt[chr(i)]++;
        }
        for(int i = 0,tmp = 0; i < K+1; i++){
            tmp += bkt[i];
            bkt[i] = end ? tmp : tmp - bkt[i];
        }
    }
    void induceSAl(byte *t, byte *s, int *bkt, int n, int K, int cs){
        getBuckets(s, bkt, n, K, cs, false);
        for(int i = 0; i < n; i++){
            if(sa[i]>0 && !tget(sa[i]-1)){
                sa[bkt[chr(sa[i]-1)]++] = sa[i]-1;
            }
        }
    }
    void induceSAs(byte *t, byte *s, int *bkt, int n, int K, int cs){
        getBuckets(s, bkt, n, K, cs, true);
        for(int i = n-1; i >= 0; i--){
            if(sa[i] > 0 && tget(sa[i]-1)){
                sa[--bkt[chr(sa[i]-1)]] = sa[i]-1;
            }
        }
    }
    void make_sa(byte *s, int n, int K=128, int cs=1){
        byte t[(n >> 3)+1];
        int bkt[K+1], n1 = 0, name = 0;
        tset(n-2, 0), tset(n-1, 1);
        for(int i = n - 3; i >=0; i--){
            tset(i, (chr(i)<chr(i+1) || (chr(i)==chr(i+1) && tget(i+1))));
        }
        getBuckets(s, bkt, n, K, cs);
        fill(sa, sa+n, -1);
        for(int i = 1; i < n; i++){
            if(isLMS(i)){
                sa[--bkt[chr(i)]] = i;
            }
        }
        induceSAl(t, s, bkt, n, K, cs);
        induceSAs(t, s, bkt, n, K, cs);
        for(int i = 0; i < n; i++){
            if(isLMS(sa[i])){
                sa[n1++] = sa[i];
            }
        }
        fill(sa + n1, sa + n, -1);
        for(int i = 0, tmp = -1; i < n1; i++){
            int pos = sa[i], diff = false;
            for(int d = 0; d < n && !diff; d++){
                diff = chr(pos+d) != chr(tmp+d) || tget(pos+d) != tget(tmp+d);
                if(!diff && d && (isLMS(pos+d) || isLMS(tmp+d))) break;
            }
            if(diff){
                name++, tmp = pos;
            }
            sa[n1+((pos - (pos & 1)) >> 1)] = name - 1;
        }
        int* s1 = sa + n - n1;
        for(int i = n - 1,j = n - 1; i >= n1; i--){
            if(sa[i] >= 0){
                sa[j--] = sa[i];
            }
        }
        if(name < n1){
            make_sa((byte*)s1, n1, name - 1, sizeof(int));
        }else{
            for(int i = 0; i < n1; i++){
                sa[s1[i]] = i;
            }
        }
        getBuckets(s, bkt, n, K, cs);
        for(int i = 1, j = 0; i < n; i++){
            if(isLMS(i)){
                s1[j++] = i;
            }
        }
        for(int i = 0; i < n1; i++){
            sa[i] = s1[sa[i]];
        }
        fill(sa + n1, sa + n, -1);
        for(int i = n1 - 1; i >= 0; i--){
          int tmp = sa[i];
          sa[i] = -1, sa[--bkt[chr(tmp)]] = tmp;
        }
        induceSAl(t, s, bkt, n, K, cs);
        induceSAs(t, s, bkt, n, K, cs);
    }
    void make_lcp(){
        lcp = new int[sz+1];
        rnk = new int[sz+1];
        for(int i = 0; i <= sz; i++) rnk[sa[i]] = i;
        lcp[0] = 0;
        for(int i = 0, h = 0; i < sz; i++){
            int j = sa[rnk[i]-1];
            if(h > 0) h--;
            for(;j+h<sz&&i+h<sz;h++){
                if(CS[j+h] != CS[i+h]) break;
            }
            lcp[rnk[i]-1] = h;
        }
    }
public:
    bool contain(const string& T){
        int a = 0, b = sz;
        while(b - a > 1){
            int c = (a + b) / 2;
            if(CS.compare(sa[c], T.length(), T) < 0){
                a = c;
            }else{
                b = c;
            }
        }
        return CS.compare(sa[b], T.length(), T) == 0;
    }
    string CS;
    byte* S;
    int sz;
    int *sa, *lcp, *rnk;
    SA_IS(string& arg){
        CS = arg;
        sz = (int)arg.size();
        sa = new int[sz+1];
        S = (byte*)arg.c_str();
        make_sa(S, sz+1);
        make_lcp();
    }
    ~SA_IS(){
        delete[] sa;
        // delete[] lcp; delete[] rnk;
    }
};

#define MAX_BIT 64

struct BitRank {
    // block: bit 列を管理, count: block ごとに立っている 1 の数を管理
    vector<unsigned long long> block;
    vector<int> count;
    BitRank(){}
    void resize(int num) {
        block.resize((num + 1) / MAX_BIT + 1, 0);
        count.resize((int)block.size(), 0);
    }
    // i ビット目を val(0,1) にセット
    inline void set(int i, unsigned long long val) {
        block[i/MAX_BIT] |= (val << (i % MAX_BIT));
    }
    void build() {
        for(int i = 1; i < (int)block.size(); i++){
            count[i] = count[i-1] + __builtin_popcountll(block[i-1]);
        }
    }
    // [0, i) ビットの 1 の数
    inline int rank1(int i) {
        int j = i/MAX_BIT, k = i % MAX_BIT;
        return count[j] + (k ? __builtin_popcountll(block[j] << (MAX_BIT-k)) : 0);
    }
    // [i, j) ビットの 1 の数
    inline int rank1(int i, int j) {
        return rank1(j) - rank1(i);
    }
    // [0, i) ビットの 0 の数
    inline int rank0(int i) {
        return i - rank1(i);
    }
    // [i, j) ビットの 0 の数
    inline int rank0(int i, int j) {
        return rank0(j) - rank0(i);
    }
};

class WaveletMatrix
{
private:
    int height;
    vector<BitRank> B;
    vector<int> pos;
public:
    WaveletMatrix(){}
    WaveletMatrix(vector<int>& vec) :
        WaveletMatrix(vec, *max_element(vec.begin(), vec.end()) + 1) {}
    // sigma:文字の種類数
    WaveletMatrix(vector<int>& vec, int sigma){
        init(vec, sigma);
    }
    void init(vector<int>& vec, int sigma){
        height = (sigma == 1) ? 1 : (MAX_BIT - __builtin_clzll(sigma-1));
        B.resize(height), pos.resize(height);
        for(int i = 0; i < height; i++){
            B[i].resize((int)vec.size());
            for(int j = 0; j < (int)vec.size(); j++) {
                B[i].set(j, get(vec[j], height - i - 1));
            }
            B[i].build();
            auto it = stable_partition(vec.begin(), vec.end(), [&](int c) {
                return !get(c, height - i - 1);
            });
            pos[i] = it - vec.begin();
        }
    }
    // val の i ビット目の値を返す(0,1)
    inline int get(int val, int i) {
        return val >> i & 1;
    }
    // [l, r) の間に現れる値 val の数
    int rank(int val, int l, int r) {
        return rank(val, r) - rank(val, l);
    }
    // [0, i) の間に現れる値 val の数
    int rank(int val, int i) {
        int p = 0;
        for(int j = 0; j < height; j++){
            if(get(val, height - j - 1)){
                p = pos[j] + B[j].rank1(p);
                i = pos[j] + B[j].rank1(i);
            }else{
                p = B[j].rank0(p);
                i = B[j].rank0(i);
            }
        }
        return i - p;
    }
    // [l, r) の k(0,1,2...) 番目に小さい値を返す
    int quantile(int k, int l, int r) {
        int res = 0;
        for(int i = 0; i < height; i++){
            int j = B[i].rank0(l, r);
            if(j > k){
                l = B[i].rank0(l);
                r = B[i].rank0(r);
            }else{
                l = pos[i] + B[i].rank1(l);
                r = pos[i] + B[i].rank1(r);
                k -= j;
                res |= (1 << (height - i - 1));
            }
        }
        return res;
    }
    int rangefreq(int i, int j, int a, int b, int l, int r, int x) {
        if(i == j || r <= a || b <= l) return 0;
        int mid = (l + r) >> 1;
        if(a <= l && r <= b){
            return j - i;
        }else{
            int left = rangefreq(B[x].rank0(i),B[x].rank0(j),a,b,l,mid,x+1);
            int right = rangefreq(pos[x]+B[x].rank1(i),pos[x]+B[x].rank1(j),a,b,mid,r,x+1);
            return left + right;
        }
    }
    // [l,r) で値が [a,b) 内に含まれる数を返す
    int rangefreq(int l, int r, int a, int b) {
        return rangefreq(l, r, a, b, 0, 1 << height, 0);
    }
    int range(int i, int j, int a, int b, int l, int r, int x, int val) {
        if(i == j || r <= a || b <= l) return -1;
        if(r - l == 1) return val;
        int mid = (l + r) >> 1;
        int res = range(B[x].rank0(i),B[x].rank0(j),a,b,l,mid,x+1,val);
        if(res < 0) return range(pos[x]+B[x].rank1(i),pos[x]+B[x].rank1(j),a,b,mid,r,x+1,val+(1 << (height - x - 1)));
        else return res;
    }
    // [l,r) で値が [a,b) 内に最小の数を返す
    int range(int l, int r, int a, int b) {
        return range(l, r, a, b, 0, 1 << height, 0, 0);
    }
};

template<typename T> class SparseTable {
private:
    vector<int> LogTable;
    vector<vector<T> > Table; //最小値を保持
    int sz;
public:
    SparseTable(vector<T>& v){
        sz = (int)v.size();
        LogTable.resize(sz+1);
        for(int i = 2; i < sz + 1; i++){
            LogTable[i] = LogTable[i >> 1] + 1;
        }
        Table.resize(sz,vector<T>(LogTable[sz]+1));
        rep(i,sz){
            Table[i][0] = v[i];
        }
        for(int k = 1; (1 << k) <= sz; k++){
            for(int i = 0; i + (1 << k) <= sz; i++){
                Table[i][k] = min(Table[i][k-1],Table[i + (1 << (k-1))][k-1]);
            }
        }
    }
    T query(int l,int r){
        int k = LogTable[r-l];
        return min(Table[l][k],Table[r-(1<<k)][k]);
    }
};

int unzip[MAX_N];
int l, r, n, q;

bool possible(int cri, SA_IS& sa, WaveletMatrix& wm, SparseTable<int>& st)
{
    int left, right;
    if(unzip[r-cri] == 0 || st.query(unzip[r-cri]-1, unzip[r-cri]) < cri){
        left = unzip[r-cri];
    }else{
        int x = -1, y = unzip[r-cri]-1;
        while(y-x>1){
            int mid = (x+y)/2;
            if(st.query(mid, unzip[r-cri]) >= cri){
                y = mid;
            }else{
                x = mid;
            }
        }
        left = y;
    }
    if(unzip[r-cri] == n-1 || st.query(unzip[r-cri], unzip[r-cri]+1) < cri){
        right = unzip[r-cri];
    }else{
        int x = unzip[r-cri]+1, y = n;
        while(y-x>1){
            int mid = (x+y)/2;
            if(st.query(unzip[r-cri], mid) >= cri){
                x = mid;
            }else{
                y = mid;
            }
        }
        right = x;
    }
    if(!wm.rangefreq(left, right+1, l+1, r-3*cri-1)) return false;
    int res = wm.range(left, right+1, l+1, r-3*cri-1);
    return wm.rangefreq(left, right+1, res+cri+1, r-2*cri);
}

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    cin >> n >> q;
    string s;
    cin >> s;
    SA_IS sa(s);
    vi vec(n), vec2(n-1);
    rep(i,n) vec[i] = sa.sa[i+1], unzip[sa.sa[i+1]] = i;
    rep(i,n-1) vec2[i] = sa.lcp[i+1];
    SparseTable<int> st(vec2);
    WaveletMatrix wm(vec);
    rep(i,q){
        cin >> l >> r;
        --l;
        if(r-l<6){
            cout << "0\n";
            continue;
        }
        int x = 0, y = (r-l-3)/3+1;
        while(y-x>1){
            int mid = (x+y)/2;
            if(possible(mid, sa, wm, st)){
                x = mid;
            }else{
                y = mid;
            }
        }
        cout << x << "\n";
    }
    return 0;
}
accuracy: 1
time_ms: 5770
memory_kb: 30428
final_rank: 11

code_snippet: #include <bits/stdc++.h>
 
using namespace std;
 
template< typename Monoid >
struct PersistentSegmentTree {
  using F = function< Monoid(Monoid, Monoid) >;
 
  struct Node {
    Monoid data;
    Node *l, *r;
 
    Node(const Monoid &data) : data(data), l(nullptr), r(nullptr) {}
  };
 
 
  int sz;
  const F f;
  const Monoid M1;
 
  PersistentSegmentTree(const F f, const Monoid &M1) : f(f), M1(M1) {}
 
  Node *build(const vector< Monoid > &v) {
    sz = (int) v.size();
    return build(0, (int) v.size(), v);
  }
 
  Node *merge(Node *l, Node *r) {
    auto t = new Node(f(l->data, r->data));
    t->l = l;
    t->r = r;
    return t;
  }
 
  Node *build(int l, int r, const vector< Monoid > &v) {
    if(l + 1 >= r) return new Node(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }
 
  Node *update(int a, const Monoid &x, Node *k, int l, int r) {
    if(r <= a || a + 1 <= l) {
      return k;
    } else if(a <= l && r <= a + 1) {
      return new Node(x);
    } else {
      return merge(update(a, x, k->l, l, (l + r) >> 1), update(a, x, k->r, (l + r) >> 1, r));
    }
  }
 
  Node *update(Node *t, int k, const Monoid &x) {
    return update(k, x, t, 0, sz);
  }
 
  Monoid query(int a, int b, Node *k, int l, int r) {
    if(r <= a || b <= l) {
      return M1;
    } else if(a <= l && r <= b) {
      return k->data;
    } else {
      return f(query(a, b, k->l, l, (l + r) >> 1),
               query(a, b, k->r, (l + r) >> 1, r));
    }
  }
 
  Monoid query(Node *t, int a, int b) {
    return query(a, b, t, 0, sz);
  }
};
 
struct SuffixArray {
  vector< int > SA;
  const string s;
 
  SuffixArray(const string &str) : s(str) {
    SA.resize(s.size());
    iota(begin(SA), end(SA), 0);
    sort(begin(SA), end(SA), [&](int a, int b) {
      return s[a] == s[b] ? a > b : s[a] < s[b];
    });
    vector< int > classes(s.size()), c(s.begin(), s.end()), cnt(s.size());
    for(int len = 1; len < s.size(); len <<= 1) {
      for(int i = 0; i < s.size(); i++) {
        if(i > 0 && c[SA[i - 1]] == c[SA[i]] && SA[i - 1] + len < s.size() && c[SA[i - 1] + len / 2] == c[SA[i] + len / 2]) {
          classes[SA[i]] = classes[SA[i - 1]];
        } else {
          classes[SA[i]] = i;
        }
      }
      iota(begin(cnt), end(cnt), 0);
      copy(begin(SA), end(SA), begin(c));
      for(int i = 0; i < s.size(); i++) {
        int s1 = c[i] - len;
        if(s1 >= 0) SA[cnt[classes[s1]]++] = s1;
      }
      classes.swap(c);
    }
  }
 
  int operator[](int k) const {
    return SA[k];
  }
 
  size_t size() const {
    return s.size();
  }
 
  bool lt_substr(const string &t, int si = 0, int ti = 0) {
    int sn = (int) s.size(), tn = (int) t.size();
    while(si < sn && ti < tn) {
      if(s[si] < t[ti]) return true;
      if(s[si] > t[ti]) return false;
      ++si, ++ti;
    }
    return si >= sn && ti < tn;
  }
 
  int lower_bound(const string &t) {
    int low = -1, high = (int) SA.size();
    while(high - low > 1) {
      int mid = (low + high) / 2;
      if(lt_substr(t, SA[mid])) low = mid;
      else high = mid;
    }
    return high;
  }
 
  pair< int, int > lower_upper_bound(string &t) {
    int idx = lower_bound(t);
    int low = idx - 1, high = (int) SA.size();
    t.back()++;
    while(high - low > 1) {
      int mid = (low + high) / 2;
      if(lt_substr(t, SA[mid])) low = mid;
      else high = mid;
    }
    t.back()--;
    return {idx, high};
  }
 
  void output() {
    for(int i = 0; i < size(); i++) {
      cout << i << ": " << s.substr(SA[i]) << endl;
    }
  }
};
 
struct LongestCommonPrefixArray {
  const SuffixArray &SA;
  vector< int > LCP, rank;
 
  LongestCommonPrefixArray(const SuffixArray &SA) : SA(SA), LCP(SA.size()) {
    rank.resize(SA.size());
    for(int i = 0; i < SA.size(); i++) {
      rank[SA[i]] = i;
    }
    for(int i = 0, h = 0; i < SA.size(); i++) {
      if(rank[i] + 1 < SA.size()) {
        for(int j = SA[rank[i] + 1]; max(i, j) + h < SA.size() && SA.s[i + h] == SA.s[j + h]; ++h);
        LCP[rank[i] + 1] = h;
        if(h > 0) --h;
      }
    }
  }
 
  int operator[](int k) const {
    return LCP[k];
  }
 
  size_t size() const {
    return LCP.size();
  }
 
  void output() {
    for(int i = 0; i < size(); i++) {
      cout << i << ": " << LCP[i] << " " << SA.s.substr(SA[i]) << endl;
    }
  }
};
 
template< typename T >
struct SparseTable {
  vector< vector< T > > st;
  vector< int > lookup;
 
  SparseTable(const vector< T > &v) {
    int b = 0;
    while((1 << b) <= v.size()) ++b;
    st.assign(b, vector< T >(1 << b));
    for(int i = 0; i < v.size(); i++) {
      st[0][i] = v[i];
    }
    for(int i = 1; i < b; i++) {
      for(int j = 0; j + (1 << i) <= (1 << b); j++) {
        st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
      }
    }
    lookup.resize(v.size() + 1);
    for(int i = 2; i < lookup.size(); i++) {
      lookup[i] = lookup[i >> 1] + 1;
    }
  }
 
  inline T rmq(int l, int r) {
    if(l >= r) return 1 << 30;
    int b = lookup[r - l];
    return min(st[b][l], st[b][r - (1 << b)]);
  }
};
 
const int INF = 1 << 30;
 
int main() {
  int N, Q;
  cin >> N >> Q;
  string S;
  cin >> S;
  reverse(begin(S), end(S));
  SuffixArray sa(S);
  LongestCommonPrefixArray lcp(sa);
 
  // lcp.output();
  vector< int > rev(N);
  for(int i = 0; i < N; i++) rev[sa[i]] = i;
  auto f = [](int a, int b) { return min(a, b); };
  using Seg = PersistentSegmentTree< int >;
  Seg seg(f, INF);
  vector< Seg::Node * > nodes;
  Seg::Node *root = seg.build(vector< int >(N, INF));
  nodes.emplace_back(root);
  for(int i = N - 1; i >= 0; i--) {
    root = seg.update(root, rev[i], i);
    nodes.emplace_back(root);
  }
  reverse(begin(nodes), end(nodes));
  SparseTable< int > ukunichia(lcp.LCP);
 
  for(int i = 0; i < Q; i++) {
    int L, R;
    cin >> L >> R;
    --L, --R;
    L = N - L - 1;
    R = N - R - 1;
    swap(L, R);
    ++R;
    auto check = [&](int v) {
 
      int low, high;
      {
        int ok = rev[L], ng = -1;
        while(ok - ng > 1) {
          int mid = (ok + ng) / 2;
          if(ukunichia.rmq(mid + 1, rev[L] + 1) >= v) ok = mid;
          else ng = mid;
        }
        low = ok;
      }
 
      {
        int ok = rev[L], ng = N + 1;
        while(ng - ok > 1) {
          int mid = (ok + ng) / 2;
          if(ukunichia.rmq(rev[L] + 1, mid) >= v) ok = mid;
          else ng = mid;
        }
        high = ok;
      }
 
      int qq = L;
      qq += v + 1;
      if(qq > R) return false;
 
      {
        int tap = seg.query(nodes[qq], low, high);
        if(tap >= INF) return false;
        qq = tap;
        qq += v + 1;
        if(qq > R) return false;
      }
 
      {
        int tap = seg.query(nodes[qq], low, high);
        if(tap >= INF) return false;
        qq = tap;
 
        qq += v + 1;
        if(qq > R) return false;
      }
 
      return qq <= R;
    };
 
 
    int ok = 0, ng = (R - L) / 3 + 1;
    while(ng - ok > 1) {
      int mid = (ok + ng) / 2;
      if(check(mid)) ok = mid;
      else ng = mid;
    }
    cout << ok << endl;
  }
}
accuracy: 1
time_ms: 2770
memory_kb: 156852
final_rank: 16

code_snippet: //
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#ifdef BTK
#define DEBUG if(1)
#else
#define CIN_ONLY if(1)
#define DEBUG if(0)
#endif
#define ALL(v) (v).begin(),(v).end()
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l, T r){bool a = l>r; if (a)l = r; return a;}
template <typename T>inline bool chmax(T &l, T r){bool a = l<r; if (a)l = r; return a;}
template <typename T>istream& operator>>(istream &is, vector<T> &v){for (auto &it : v)is >> it;return is;}
class reverse_range {private:struct I {int x;int operator*() {return x-1;}bool operator!=(I& lhs) {return x>lhs.x;}void operator++() {--x;}};I i, n;public:reverse_range(int n) :i({ 0 }), n({ n }){}reverse_range(int i, int n) :i({ i }), n({ n }){}I& begin() {return n;}I& end() {return i;}};
class range {private: struct I { int x; int operator*() { return x; }bool operator!=(I& lhs) { return x<lhs.x; }void operator++() { ++x; } }; I i, n;public:range(int n) :i({ 0 }), n({ n }) {}range(int i, int n) :i({ i }), n({ n }) {}I& begin() { return i; }I& end() { return n; }reverse_range operator!(){return reverse_range(*i,*n);}};

int N,Q;
char buf[212345];
string S;


/*
  sa[0]=n //空文字列
  lcp[i]:=suffix[sa[i]]とsuffix[sa[i+1]]の共通高さ
 */

namespace latte{
    void create_begin_bucket(vector<int>&v,vector<int>&bucket){
        fill(bucket.begin(),bucket.end(),0);
        for(int i=0;i<(int)v.size();i++)bucket[v[i]]++;
        int sum=0;
        for(int i=0;i<(int)bucket.size();i++){bucket[i]+=sum;swap(sum,bucket[i]);}
    }

    void create_end_bucket(vector<int>&v,vector<int>&bucket){
        fill(bucket.begin(),bucket.end(),0);
        for(int i=0;i<(int)v.size();i++)bucket[v[i]]++;
        for(int i=1;i<(int)bucket.size();i++)bucket[i]+=bucket[i-1];
    }

    void induced_sort(vector<int>&v,vector<int>&sa,int mv,vector<int>&bucket,vector<int>&is_l){
        create_begin_bucket(v,bucket);
        for(int i=0;i<(int)v.size();i++)if(sa[i]>0&&is_l[sa[i]-1])sa[bucket[v[sa[i]-1]]++]=sa[i]-1;
    }

    void invert_induced_sort(vector<int>&v,vector<int>&sa,int mv,vector<int>&bucket,vector<int>&is_l){
        create_end_bucket(v,bucket);
        for(int i=v.size()-1;i>=0;i--)if(sa[i]>0&&!is_l[sa[i]-1])sa[--bucket[v[sa[i]-1]]]=sa[i]-1;
    }

    vector<int>sa_is(vector<int>v,int mv){
        if(v.size()==1)return vector<int>(1,0);

        vector<int>is_l(v.size());
        vector<int>bucket(mv+1);
        vector<int>sa(v.size(),-1);
        auto is_lms=[&](int x)->bool{return x>0&&is_l[x-1]&&!is_l[x];};

        is_l[v.size()-1]=0;
        for(int i=v.size()-2;i>=0;i--)is_l[i]=v[i]>v[i+1]||(v[i]==v[i+1]&&is_l[i+1]);
        create_end_bucket(v,bucket);
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))sa[--bucket[v[i]]]=i;
        induced_sort(v,sa,mv,bucket,is_l);
        invert_induced_sort(v,sa,mv,bucket,is_l);

        int cur=0;
        vector<int>order(v.size());
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))order[i]=cur++;

        vector<int>next_v(cur);
        cur=-1;
        int prev=-1;
        for(int i=0;i<(int)v.size();i++){
            if(!is_lms(sa[i]))continue;
            bool diff=false;
            for(int d=0;d<v.size();d++){
                if(prev==-1||v[sa[i]+d]!=v[prev+d]||is_l[sa[i]+d]!=is_l[prev+d]){
                    diff=true;
                    break;
                }
                else if(d>0&&is_lms(sa[i]+d))break;
            }
            if(diff){cur++;prev=sa[i];}
            next_v[order[sa[i]]]=cur;
        }

        vector<int>re_order(next_v.size());
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))re_order[order[i]]=i;
        vector<int>next_sa=sa_is(next_v,cur);
        create_end_bucket(v,bucket);
        for(int i=0;i<sa.size();i++)sa[i]=-1;
        for(int i=next_sa.size()-1;i>=0;i--)sa[--bucket[v[re_order[next_sa[i]]]]]=re_order[next_sa[i]];
        induced_sort(v,sa,mv,bucket,is_l);
        invert_induced_sort(v,sa,mv,bucket,is_l);
        return sa;
    }

    vector<int> sa_is(string &s){
        vector<int>v(s.size()+1);
        for(int i=0;i<(int)s.size();i++)v[i]=s[i];
        return sa_is(v,*max_element(v.begin(),v.end()));
    }

    
}
namespace SA {
    int n, k;
    int R[500000];
    int T[500000];
    bool compare_sa(int i, int j) {
        if (R[i] != R[j])return R[i] < R[j];
        else {
            int ri = i + k <= n ? R[i + k] : -1;
            int rj = j + k <= n ? R[j + k] : -1;
            return ri < rj;
        }
    }
    vector<int> construct_sa(string& S) {
        return latte::sa_is(S);
        n = S.size();
        vector<int> sa(n + 1);
        for (int i = 0; i <= n; i++) {
            sa[i] = i;
            R[i] = i < n ? S[i] : -1;
        }
 
        for (k = 1; k <= n; k *= 2) {
            sort(sa.begin(), sa.end(), compare_sa);
            T[sa[0]] = 0;
            for (int i = 1; i <= n; i++) 
                T[sa[i]] = T[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
            for (int i = 0; i <= n; i++)
                R[i] = T[i];
        }
        return sa;
    }
    vector<int> construct_lcp(string& S, vector<int> &sa) {
        n = S.size();
        for (int i = 0; i <= n; i++)R[sa[i]] = i;
        int h = 0;
        vector<int> lcp(n + 1, 0);
        for (int i = 0; i < n; i++) {
            int j = sa[R[i] - 1];
            if (h > 0)h--;
            for (; j + h < n&&i + h < n; h++) {
                if (S[j + h] != S[i + h])break;
            }
            lcp[R[i] - 1] = h;
        }
        return lcp;
    }
}

int ret[212345];

vector<int> sa,lcp;
vector<int> id;


namespace StaticRMQ {
	/*
	default:: range minimum query
	*/

	template<typename T>
	using MergeFunction = function<T(T,T)>;

	template<typename T>
	MergeFunction<T>
		getMin = [](T l, T r) {
		return l < r ? l : r;
	};

    template<typename T>
	MergeFunction<T>
		getMax = [](T l, T r) {
		return l > r ? l : r;
	};


	template<typename T>
	class BufferManager {
	private:
		T* const mem;
		int ptr;
	public:
		BufferManager(T* buf):mem(buf) {
			ptr = 0;
		}
		T* get(int m) {
			ptr += m;
			return mem + ptr - m;
		}
		void reset() {
			ptr = 0;
		}
	};

	namespace Buffer{
		// if N<=10^6 : BufferSize is enough for 5 * N
		constexpr int BufferSize = 5 * 1123456;
		using NodeType = int;
		NodeType mem[BufferSize];
		BufferManager<NodeType> buffer(mem);
	}

	template<typename T, typename ITR>
	struct SparseTable {
		const int n;
		const int logn;
		T* const mem;
		MergeFunction<T> Merge;
		void build() {
			for (int h = 0; h + 1 < logn; h++) {
				const int half = 1 << h;
				const int len = half << 1;
				for (int i = 0; i + len <= n; i++) {
					mem[h*n + n + i] = Merge(mem[h*n + i], mem[h*n + i + half]);
				}
			}
		}
		static inline int get_log(const int n) {
			int l = 1;
			for (int tmp = 1; tmp < n; tmp <<= 1)l++;
			return l;
		}
		SparseTable(ITR bg, ITR ed,
            MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
            )
			: n(distance(bg,ed)),
			logn(get_log(n)),
			mem(bf.get(n*logn)),
			Merge(f) {
			for (auto p = mem; bg != ed; ++bg, ++p) {
				(*p) = (*bg);
			}
			build();
		}
		inline int get_msb(const int size) {
#ifdef BTK
			int id = 0;
			for (int i = 0; i < logn; i++)if (size >> i)id = i;
			return id;
#else
			return 31 - __builtin_clz(size);
#endif
		}
		inline T get(const int l, const int r) {
			const int msb = get_msb(r - l);
			return Merge(mem[msb*n + l], mem[msb*n + r - (1 << msb)]);
		}
	};

	template<typename T, typename ITR>
	struct SmallRMQ{
		const int n;
		T* const mem;
		MergeFunction<T> Merge;
		void build(ITR bg,ITR ed) {
			int x = distance(bg, ed);
			for (auto p = mem; x > 0; x -= 4) {
				T tmp[4];
				tmp[0] = (*bg); ++bg;
				if (x >= 1)tmp[1] = (*bg); ++bg;
				if (x >= 2)tmp[2] = (*bg); ++bg;
				if (x >= 3)tmp[3] = (*bg); ++bg;
				for (int i = 0; i < 4; i++) {
					T ret = tmp[i];
					(*p) = ret; ++p;
					for (int j = i + 1; j < 4; j++) {
						ret = Merge(ret,tmp[j]);
						(*p) = ret; ++p;
					}
				}
			}
		}
		SmallRMQ(ITR bg, ITR ed,
			MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
            )
            : n(((distance(bg, ed)+3)>>2)<<2),
			mem(bf.get((n>>2) * 10)),
			Merge(f) {
			build(bg, ed);
		}
		inline T get(int l, int r) {
			const int block = l >> 2;
			l &= 3; r--; r &= 3;
			const int segment = (10 - (((4 - l)*(4 - l + 1)) >> 1)) + (r - l);
			return mem[block * 10 + segment];
		}

	};
	template<typename T, typename ITR>
	struct RMQ {
		const int n;
		MergeFunction<T> Merge;
		T* const workspace;
		SmallRMQ<T, ITR> small;
		SmallRMQ<T, T*> medium;
		SparseTable<T, T*> large;
		template<typename S, typename Itr>
		inline S buildBlock(Itr bg, Itr ed, BufferManager<T>& bf, const int size) {
			T* p = workspace;
			for (int x = distance(bg, ed); x > 0; ++p, x-=4) {
				(*p) = (*bg); ++bg;
				if(x>=1)(*p) = Merge(*p, *bg); ++bg;
				if(x>=2)(*p) = Merge(*p, *bg); ++bg;
				if(x>=3)(*p) = Merge(*p, *bg); ++bg;
			}
			return S(workspace, workspace+size, Merge, bf);
		}
		RMQ(ITR bg, ITR ed,
			MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
        ): n(((distance(bg, ed) + 15) >> 4) << 4),
			Merge(f),
			workspace(bf.get(n >> 2)),
			small(bg, ed, f, bf),
			medium(buildBlock<SmallRMQ<T, T*>, ITR>(bg, ed, bf, n >> 2)),
			large(buildBlock<SparseTable<T, T*>, T*>(workspace, workspace + (n >> 2), bf, n >> 4))
		{}
		inline T get(int l, int r) {
			int nl = (l >> 2) + 1;
			int nr = ((r - 1) >> 2);
			T ret = small.get(l, min(nl << 2, r));
			ret = Merge(ret, small.get(max(l, nr << 2),r));
			l = nl; r = nr;
			nl = (l >> 2) + 1; 
			nr = ((r - 1) >> 2);
			if (l >= r)return ret;
			ret = Merge(ret, medium.get(l, min(nl << 2, r)));
			ret = Merge(ret, medium.get(max(l, nr << 2), r));
			l = nl; r = nr;
			if (l >= r)return ret;
			else return Merge(ret, large.get(l, r));
		}
	};
}
using RMQ_PTR=StaticRMQ::RMQ<int,decltype(lcp.begin())>;


inline pair<int,int> get_range(int pos,int len,RMQ_PTR&rmq){
    int lb=pos;
    int ub=pos+1;
    {
        int ok=pos;
        int ng=-1;
        while(abs(ok-ng)>1){
            const int mid=(ok+ng)/2;
            if(rmq.get(mid,pos)>=len){
                ok=mid;
            }
            else{
                ng=mid;
            }
        }
        chmin(lb,ok);
    }
    {
        int ok=pos;
        int ng=N+1;
        while(abs(ok-ng)>1){
            const int mid=(ok+ng)/2;
            if(rmq.get(pos,mid)>=len){
                ok=mid;
            }
            else{
                ng=mid;
            }
        }
        chmax(ub,ng);
    }
    return {lb,ub};
}


constexpr int INF = 1e7;
template<typename SEG> using SetInitialLeaf = function<void(SEG&, int)>;
template<typename SEG> using SetInitialSegment = function<void(SEG&, const int, const int)>;
template<typename RET, typename SEG> using GetSingleSegment = function<RET(SEG&, const int, const int)>;
template<typename RET> using GetMergedSegment = function<RET(RET, RET)>;
template<typename SEG, typename Q> using UpdateSingleSegment = function<void(SEG&, Q)>;
template<typename SEG> using LazyUpdate = function<void(SEG&, SEG&, SEG&)>;

struct node { vector<int> x; };

int query_lb;

SetInitialLeaf<node>
set_leaf = [](node &v, const int id) {
};

SetInitialSegment<node>
set_segment = [](node &v, const int l, const int r) {
    const int lb = l;
    const int ub = min(N+1,r);
    v.x.reserve(max(ub-lb,0)+1);
    for(int i:range(lb,ub)){
        v.x.push_back(sa[i]);
    }
    v.x.push_back(INF);
    sort(ALL(v.x));
};

GetSingleSegment<int, node>
segment_min = [](node &v, const int l, const int r) {
	return *lower_bound(ALL(v.x),query_lb);
};

GetMergedSegment<int>
merge_segment = [](int l, int r) {
	return l < r ? l : r;
};


#ifndef NDBUF
#define NDBUF
template<typename T>
struct BufferManager {
	T *mem;
	int ptr;
	BufferManager(T* mem) {
		ptr = 0;
		this->mem = mem;
	}
	T* get(int m) {
		ptr += m;
		return mem + ptr - m;
	}
	void reset() {
		ptr = 0;
	}
};


#endif
namespace H {
	constexpr int BufferSize = 812345;
	using NodeType = node;
	NodeType mem[BufferSize];
	BufferManager<NodeType> buffer(mem);
}
template<typename Node, typename RET>
struct SegmentTree {
	int size;
	Node *seg;
	GetSingleSegment<RET, Node> get_single_segment;
	GetMergedSegment<RET> get_merged_segment;
	LazyUpdate<Node> lazy_update;
	void init(int l, int r, SetInitialSegment<Node>& init_segment, SetInitialLeaf<Node>& init_leaf, int k = 0) {
		auto &v = seg[k];
		init_segment(v, l, r);
		if (r - l == 1) {
			//葉の時の処理
			init_leaf(v, l);
		}
		else if (r - l>1) {
			int m = (l + r) / 2;
			init(l, m, init_segment, init_leaf, k * 2 + 1);
			init(m, r, init_segment, init_leaf, k * 2 + 2);
		}
	}
	SegmentTree(
		int n,
		GetSingleSegment<RET, Node> gss,
		GetMergedSegment<RET> gms,
		SetInitialSegment<Node> sis,
		SetInitialLeaf<Node> sil,
		LazyUpdate<Node> lu = [](Node &v,Node &l,Node &r){},
		BufferManager<Node>& buf = H::buffer
	)
		:get_single_segment(gss),
		get_merged_segment(gms),
		lazy_update(lu) {
		size = 1; while (size<n)size *= 2;
		seg = buf.get(size * 2 + 10);
		init(0, size, sis, sil);
	}
    SegmentTree(){
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
	RET get(int a, int b, int k, int l, int r) {
		if (a <= l && r <= b)return get_single_segment(seg[k], l, r);
		if (r - l > 1)lazy_update(seg[k], seg[2 * k + 1], seg[2 * k + 2]);
		int m = (l + r) / 2;
		bool ll = !(m <= a || b <= l);
		bool rr = !(r <= a || b <= m);
		RET ret;
		if (ll&&rr)ret = get_merged_segment(get(LQ), get(RQ));
		else if (ll)ret = get(LQ); else ret = get(RQ);
		
		return ret;
	}
	RET get(int a, int b) {
		return get(a, b, 0, 0, size);
	}
	template<typename Q>
	void update(int a, int b, int k, int l, int r, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		if (r <= a || b <= l)return;
		if (a <= l && r <= b) {
			update_segment(seg[k], value);
		}
		else {
			if (r - l > 1)lazy_update(seg[k], seg[2 * k + 1], seg[2 * k + 2]);
			int m = (l + r) / 2;
			update(LQ, update_segment, value);
			update(RQ, update_segment, value);
			seg[k].ret = get_merged_segment(
				get_single_segment(seg[k * 2 + 1], l, m),
				get_single_segment(seg[k * 2 + 2], m, r)
			);
		}
	}
	template<typename Q>
	void update_segment(int a, int b, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		update(a, b, 0, 0, size, update_segment, value);
	}

	template<typename Q>
	void update_element(int a, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		update(a, a + 1, 0, 0, size, update_segment, value);
	}
};

SegmentTree<node,int> st;



inline bool check(int ql,int qr,int X_len,RMQ_PTR&rmq){
    if(X_len*3+3>qr-ql)return false;
    int lb,ub;
    tie(lb,ub)=get_range(id[ql],X_len,rmq);
    int p;
    {
        int X1=ql+X_len;
        query_lb = X1+1;
        p = st.get(lb,ub);
        int A1=p;
        int X2=p+X_len;
        if(X2>=N)return false;
        query_lb = X2+1;
        p = st.get(lb,ub);
        int A2=p;
        int X3=p+X_len;
        if(X3>=N)return false;
        int A3=qr;

        return A3-X3>=1;
    }
}





inline int solve(int ql,int qr,RMQ_PTR&rmq){
    int lb=0;
    int ub=qr-ql;
    while(ub-lb>1){
        const int mid = (lb+ub)/2;
        if(check(ql,qr,mid,rmq)){
            lb=mid;
        }
        else{
            ub=mid;
        }
    }
    return lb;
}




int main(){
    scanf("%d%d%s",&N,&Q,buf);
    reverse(buf,buf+N);
    S=buf;
    sa=SA::construct_sa(S);
    id=vector<int>(N+1);
    for(int i:range(N+1)){
        id[sa[i]]=i;
    }
    lcp=SA::construct_lcp(S,sa);
    RMQ_PTR rmq(lcp.begin(),lcp.end());
    st = SegmentTree<node,int>(N+1,segment_min, merge_segment, set_segment, set_leaf);


    for(int i:range(Q)){
        int l,r;
        scanf("%d%d",&l,&r);


        ret[i]=solve(N-r,N-l+1,rmq);
    }
    for(int i:range(Q)){
        printf("%d\n",ret[i]);
    }
    return 0;
}
accuracy: 1
time_ms: 4980
memory_kb: 57728
final_rank: 12

code_snippet: //
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#ifdef BTK
#define DEBUG if(1)
#else
#define CIN_ONLY if(1)
#define DEBUG if(0)
#endif
#define ALL(v) (v).begin(),(v).end()
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l, T r){bool a = l>r; if (a)l = r; return a;}
template <typename T>inline bool chmax(T &l, T r){bool a = l<r; if (a)l = r; return a;}
template <typename T>istream& operator>>(istream &is, vector<T> &v){for (auto &it : v)is >> it;return is;}
class reverse_range {private:struct I {int x;int operator*() {return x-1;}bool operator!=(I& lhs) {return x>lhs.x;}void operator++() {--x;}};I i, n;public:reverse_range(int n) :i({ 0 }), n({ n }){}reverse_range(int i, int n) :i({ i }), n({ n }){}I& begin() {return n;}I& end() {return i;}};
class range {private: struct I { int x; int operator*() { return x; }bool operator!=(I& lhs) { return x<lhs.x; }void operator++() { ++x; } }; I i, n;public:range(int n) :i({ 0 }), n({ n }) {}range(int i, int n) :i({ i }), n({ n }) {}I& begin() { return i; }I& end() { return n; }reverse_range operator!(){return reverse_range(*i,*n);}};

int N,Q;
char buf[212345];
string S;


/*
  sa[0]=n //空文字列
  lcp[i]:=suffix[sa[i]]とsuffix[sa[i+1]]の共通高さ
 */

namespace latte{
    void create_begin_bucket(vector<int>&v,vector<int>&bucket){
        fill(bucket.begin(),bucket.end(),0);
        for(int i=0;i<(int)v.size();i++)bucket[v[i]]++;
        int sum=0;
        for(int i=0;i<(int)bucket.size();i++){bucket[i]+=sum;swap(sum,bucket[i]);}
    }

    void create_end_bucket(vector<int>&v,vector<int>&bucket){
        fill(bucket.begin(),bucket.end(),0);
        for(int i=0;i<(int)v.size();i++)bucket[v[i]]++;
        for(int i=1;i<(int)bucket.size();i++)bucket[i]+=bucket[i-1];
    }

    void induced_sort(vector<int>&v,vector<int>&sa,int mv,vector<int>&bucket,vector<int>&is_l){
        create_begin_bucket(v,bucket);
        for(int i=0;i<(int)v.size();i++)if(sa[i]>0&&is_l[sa[i]-1])sa[bucket[v[sa[i]-1]]++]=sa[i]-1;
    }

    void invert_induced_sort(vector<int>&v,vector<int>&sa,int mv,vector<int>&bucket,vector<int>&is_l){
        create_end_bucket(v,bucket);
        for(int i=v.size()-1;i>=0;i--)if(sa[i]>0&&!is_l[sa[i]-1])sa[--bucket[v[sa[i]-1]]]=sa[i]-1;
    }

    vector<int>sa_is(vector<int>v,int mv){
        if(v.size()==1)return vector<int>(1,0);

        vector<int>is_l(v.size());
        vector<int>bucket(mv+1);
        vector<int>sa(v.size(),-1);
        auto is_lms=[&](int x)->bool{return x>0&&is_l[x-1]&&!is_l[x];};

        is_l[v.size()-1]=0;
        for(int i=v.size()-2;i>=0;i--)is_l[i]=v[i]>v[i+1]||(v[i]==v[i+1]&&is_l[i+1]);
        create_end_bucket(v,bucket);
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))sa[--bucket[v[i]]]=i;
        induced_sort(v,sa,mv,bucket,is_l);
        invert_induced_sort(v,sa,mv,bucket,is_l);

        int cur=0;
        vector<int>order(v.size());
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))order[i]=cur++;

        vector<int>next_v(cur);
        cur=-1;
        int prev=-1;
        for(int i=0;i<(int)v.size();i++){
            if(!is_lms(sa[i]))continue;
            bool diff=false;
            for(int d=0;d<v.size();d++){
                if(prev==-1||v[sa[i]+d]!=v[prev+d]||is_l[sa[i]+d]!=is_l[prev+d]){
                    diff=true;
                    break;
                }
                else if(d>0&&is_lms(sa[i]+d))break;
            }
            if(diff){cur++;prev=sa[i];}
            next_v[order[sa[i]]]=cur;
        }

        vector<int>re_order(next_v.size());
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))re_order[order[i]]=i;
        vector<int>next_sa=sa_is(next_v,cur);
        create_end_bucket(v,bucket);
        for(int i=0;i<sa.size();i++)sa[i]=-1;
        for(int i=next_sa.size()-1;i>=0;i--)sa[--bucket[v[re_order[next_sa[i]]]]]=re_order[next_sa[i]];
        induced_sort(v,sa,mv,bucket,is_l);
        invert_induced_sort(v,sa,mv,bucket,is_l);
        return sa;
    }

    vector<int> sa_is(string &s){
        vector<int>v(s.size()+1);
        for(int i=0;i<(int)s.size();i++)v[i]=s[i];
        return sa_is(v,*max_element(v.begin(),v.end()));
    }

    
}
namespace SA {
    int n, k;
    int R[500000];
    int T[500000];
    bool compare_sa(int i, int j) {
        if (R[i] != R[j])return R[i] < R[j];
        else {
            int ri = i + k <= n ? R[i + k] : -1;
            int rj = j + k <= n ? R[j + k] : -1;
            return ri < rj;
        }
    }
    vector<int> construct_sa(string& S) {
        return latte::sa_is(S);
        n = S.size();
        vector<int> sa(n + 1);
        for (int i = 0; i <= n; i++) {
            sa[i] = i;
            R[i] = i < n ? S[i] : -1;
        }
 
        for (k = 1; k <= n; k *= 2) {
            sort(sa.begin(), sa.end(), compare_sa);
            T[sa[0]] = 0;
            for (int i = 1; i <= n; i++) 
                T[sa[i]] = T[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
            for (int i = 0; i <= n; i++)
                R[i] = T[i];
        }
        return sa;
    }
    vector<int> construct_lcp(string& S, vector<int> &sa) {
        n = S.size();
        for (int i = 0; i <= n; i++)R[sa[i]] = i;
        int h = 0;
        vector<int> lcp(n + 1, 0);
        for (int i = 0; i < n; i++) {
            int j = sa[R[i] - 1];
            if (h > 0)h--;
            for (; j + h < n&&i + h < n; h++) {
                if (S[j + h] != S[i + h])break;
            }
            lcp[R[i] - 1] = h;
        }
        return lcp;
    }
}

int ret[212345];

vector<int> sa,lcp;
vector<int> id;


namespace StaticRMQ {
	/*
	default:: range minimum query
	*/

	template<typename T>
	using MergeFunction = function<T(T,T)>;

	template<typename T>
	MergeFunction<T>
		getMin = [](T l, T r) {
		return l < r ? l : r;
	};

    template<typename T>
	MergeFunction<T>
		getMax = [](T l, T r) {
		return l > r ? l : r;
	};


	template<typename T>
	class BufferManager {
	private:
		T* const mem;
		int ptr;
	public:
		BufferManager(T* buf):mem(buf) {
			ptr = 0;
		}
		T* get(int m) {
			ptr += m;
			return mem + ptr - m;
		}
		void reset() {
			ptr = 0;
		}
	};

	namespace Buffer{
		// if N<=10^6 : BufferSize is enough for 5 * N
		constexpr int BufferSize = 5 * 1123456;
		using NodeType = int;
		NodeType mem[BufferSize];
		BufferManager<NodeType> buffer(mem);
	}

	template<typename T, typename ITR>
	struct SparseTable {
		const int n;
		const int logn;
		T* const mem;
		MergeFunction<T> Merge;
		void build() {
			for (int h = 0; h + 1 < logn; h++) {
				const int half = 1 << h;
				const int len = half << 1;
				for (int i = 0; i + len <= n; i++) {
					mem[h*n + n + i] = Merge(mem[h*n + i], mem[h*n + i + half]);
				}
			}
		}
		static inline int get_log(const int n) {
			int l = 1;
			for (int tmp = 1; tmp < n; tmp <<= 1)l++;
			return l;
		}
		SparseTable(ITR bg, ITR ed,
            MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
            )
			: n(distance(bg,ed)),
			logn(get_log(n)),
			mem(bf.get(n*logn)),
			Merge(f) {
			for (auto p = mem; bg != ed; ++bg, ++p) {
				(*p) = (*bg);
			}
			build();
		}
		inline int get_msb(const int size) {
#ifdef BTK
			int id = 0;
			for (int i = 0; i < logn; i++)if (size >> i)id = i;
			return id;
#else
			return 31 - __builtin_clz(size);
#endif
		}
		inline T get(const int l, const int r) {
			const int msb = get_msb(r - l);
			return Merge(mem[msb*n + l], mem[msb*n + r - (1 << msb)]);
		}
	};

	template<typename T, typename ITR>
	struct SmallRMQ{
		const int n;
		T* const mem;
		MergeFunction<T> Merge;
		void build(ITR bg,ITR ed) {
			int x = distance(bg, ed);
			for (auto p = mem; x > 0; x -= 4) {
				T tmp[4];
				tmp[0] = (*bg); ++bg;
				if (x >= 1)tmp[1] = (*bg); ++bg;
				if (x >= 2)tmp[2] = (*bg); ++bg;
				if (x >= 3)tmp[3] = (*bg); ++bg;
				for (int i = 0; i < 4; i++) {
					T ret = tmp[i];
					(*p) = ret; ++p;
					for (int j = i + 1; j < 4; j++) {
						ret = Merge(ret,tmp[j]);
						(*p) = ret; ++p;
					}
				}
			}
		}
		SmallRMQ(ITR bg, ITR ed,
			MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
            )
            : n(((distance(bg, ed)+3)>>2)<<2),
			mem(bf.get((n>>2) * 10)),
			Merge(f) {
			build(bg, ed);
		}
		inline T get(int l, int r) {
			const int block = l >> 2;
			l &= 3; r--; r &= 3;
			const int segment = (10 - (((4 - l)*(4 - l + 1)) >> 1)) + (r - l);
			return mem[block * 10 + segment];
		}

	};
	template<typename T, typename ITR>
	struct RMQ {
		const int n;
		MergeFunction<T> Merge;
		T* const workspace;
		SmallRMQ<T, ITR> small;
		SmallRMQ<T, T*> medium;
		SparseTable<T, T*> large;
		template<typename S, typename Itr>
		inline S buildBlock(Itr bg, Itr ed, BufferManager<T>& bf, const int size) {
			T* p = workspace;
			for (int x = distance(bg, ed); x > 0; ++p, x-=4) {
				(*p) = (*bg); ++bg;
				if(x>=1)(*p) = Merge(*p, *bg); ++bg;
				if(x>=2)(*p) = Merge(*p, *bg); ++bg;
				if(x>=3)(*p) = Merge(*p, *bg); ++bg;
			}
			return S(workspace, workspace+size, Merge, bf);
		}
		RMQ(ITR bg, ITR ed,
			MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
        ): n(((distance(bg, ed) + 15) >> 4) << 4),
			Merge(f),
			workspace(bf.get(n >> 2)),
			small(bg, ed, f, bf),
			medium(buildBlock<SmallRMQ<T, T*>, ITR>(bg, ed, bf, n >> 2)),
			large(buildBlock<SparseTable<T, T*>, T*>(workspace, workspace + (n >> 2), bf, n >> 4))
		{}
		inline T get(int l, int r) {
			int nl = (l >> 2) + 1;
			int nr = ((r - 1) >> 2);
			T ret = small.get(l, min(nl << 2, r));
			ret = Merge(ret, small.get(max(l, nr << 2),r));
			l = nl; r = nr;
			nl = (l >> 2) + 1; 
			nr = ((r - 1) >> 2);
			if (l >= r)return ret;
			ret = Merge(ret, medium.get(l, min(nl << 2, r)));
			ret = Merge(ret, medium.get(max(l, nr << 2), r));
			l = nl; r = nr;
			if (l >= r)return ret;
			else return Merge(ret, large.get(l, r));
		}
	};
}
using RMQ_PTR=StaticRMQ::RMQ<int,decltype(lcp.begin())>;


inline pair<int,int> get_range(int pos,int len,RMQ_PTR&rmq){
    int lb=pos;
    int ub=pos+1;
    {
        int ok=pos;
        int ng=-1;
        while(abs(ok-ng)>1){
            const int mid=(ok+ng)/2;
            if(rmq.get(mid,pos)>=len){
                ok=mid;
            }
            else{
                ng=mid;
            }
        }
        chmin(lb,ok);
    }
    {
        int ok=pos;
        int ng=N+1;
        while(abs(ok-ng)>1){
            const int mid=(ok+ng)/2;
            if(rmq.get(pos,mid)>=len){
                ok=mid;
            }
            else{
                ng=mid;
            }
        }
        chmax(ub,ng);
    }
    return {lb,ub};
}


constexpr int INF = 1e7;
template<typename SEG> using SetInitialLeaf = function<void(SEG&, int)>;
template<typename SEG> using SetInitialSegment = function<void(SEG&, const int, const int)>;
template<typename RET, typename SEG> using GetSingleSegment = function<RET(SEG&, const int, const int)>;
template<typename RET> using GetMergedSegment = function<RET(RET, RET)>;
template<typename SEG, typename Q> using UpdateSingleSegment = function<void(SEG&, Q)>;
template<typename SEG> using LazyUpdate = function<void(SEG&, SEG&, SEG&)>;

struct node { vector<int> x; };

int query_lb;

SetInitialLeaf<node>
set_leaf = [](node &v, const int id) {
};

SetInitialSegment<node>
set_segment = [](node &v, const int l, const int r) {
    const int lb = l;
    const int ub = min(N+1,r);
    v.x.reserve(max(ub-lb,0)+1);
    for(int i:range(lb,ub)){
        v.x.push_back(sa[i]);
    }
    v.x.push_back(INF);
    sort(ALL(v.x));
};

GetSingleSegment<int, node>
segment_min = [](node &v, const int l, const int r) {
	return *lower_bound(ALL(v.x),query_lb);
};

GetMergedSegment<int>
merge_segment = [](int l, int r) {
	return l < r ? l : r;
};


#ifndef NDBUF
#define NDBUF
template<typename T>
struct BufferManager {
	T *mem;
	int ptr;
	BufferManager(T* mem) {
		ptr = 0;
		this->mem = mem;
	}
	T* get(int m) {
		ptr += m;
		return mem + ptr - m;
	}
	void reset() {
		ptr = 0;
	}
};


#endif
namespace H {
	constexpr int BufferSize = 812345;
	using NodeType = node;
	NodeType mem[BufferSize];
	BufferManager<NodeType> buffer(mem);
}
template<typename Node, typename RET>
struct SegmentTree {
	int size;
	Node *seg;
	GetSingleSegment<RET, Node> get_single_segment;
	GetMergedSegment<RET> get_merged_segment;
	LazyUpdate<Node> lazy_update;
	void init(int l, int r, SetInitialSegment<Node>& init_segment, SetInitialLeaf<Node>& init_leaf, int k = 0) {
		auto &v = seg[k];
		init_segment(v, l, r);
		if (r - l == 1) {
			//葉の時の処理
			init_leaf(v, l);
		}
		else if (r - l>1) {
			int m = (l + r) / 2;
			init(l, m, init_segment, init_leaf, k * 2 + 1);
			init(m, r, init_segment, init_leaf, k * 2 + 2);
		}
	}
	SegmentTree(
		int n,
		GetSingleSegment<RET, Node> gss,
		GetMergedSegment<RET> gms,
		SetInitialSegment<Node> sis,
		SetInitialLeaf<Node> sil,
		LazyUpdate<Node> lu = [](Node &v,Node &l,Node &r){},
		BufferManager<Node>& buf = H::buffer
	)
		:get_single_segment(gss),
		get_merged_segment(gms),
		lazy_update(lu) {
		size = 1; while (size<n)size *= 2;
		seg = buf.get(size * 2 + 10);
		init(0, size, sis, sil);
	}
    SegmentTree(){
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
	RET get(int a, int b, int k, int l, int r) {
		if (a <= l && r <= b)return get_single_segment(seg[k], l, r);
		if (r - l > 1)lazy_update(seg[k], seg[2 * k + 1], seg[2 * k + 2]);
		int m = (l + r) / 2;
		bool ll = !(m <= a || b <= l);
		bool rr = !(r <= a || b <= m);
		RET ret;
		if (ll&&rr)ret = get_merged_segment(get(LQ), get(RQ));
		else if (ll)ret = get(LQ); else ret = get(RQ);
		
		return ret;
	}
	RET get(int a, int b) {
		return get(a, b, 0, 0, size);
	}
	template<typename Q>
	void update(int a, int b, int k, int l, int r, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		if (r <= a || b <= l)return;
		if (a <= l && r <= b) {
			update_segment(seg[k], value);
		}
		else {
			if (r - l > 1)lazy_update(seg[k], seg[2 * k + 1], seg[2 * k + 2]);
			int m = (l + r) / 2;
			update(LQ, update_segment, value);
			update(RQ, update_segment, value);
			seg[k].ret = get_merged_segment(
				get_single_segment(seg[k * 2 + 1], l, m),
				get_single_segment(seg[k * 2 + 2], m, r)
			);
		}
	}
	template<typename Q>
	void update_segment(int a, int b, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		update(a, b, 0, 0, size, update_segment, value);
	}

	template<typename Q>
	void update_element(int a, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		update(a, a + 1, 0, 0, size, update_segment, value);
	}
};

SegmentTree<node,int> st;



inline bool check(int ql,int qr,int X_len,RMQ_PTR&rmq,bool db=false){
    if(X_len*3+3>qr-ql)return false;
    int lb,ub;
    tie(lb,ub)=get_range(id[ql],X_len,rmq);
    int p;
    {
        int X1=ql+X_len;
        query_lb = X1+1;
        p = st.get(lb,ub);
        int A1=p;
        int X2=p+X_len;
        if(X2>=N)return false;
        query_lb = X2+1;
        p = st.get(lb,ub);
        int A2=p;
        int X3=p+X_len;
        if(X3>=N)return false;
        int A3=qr;
        if(A3-X3>=1){
            /*
            if(db){
            cerr << S.substr(ql,X1-ql) << " ";
            cerr << S.substr(X1,A1-X1) << " ";
            cerr << S.substr(A1,X2-A1) << " ";
            cerr << S.substr(X2,A2-X2) << " ";
            cerr << S.substr(A2,X3-A2) << " ";
            cerr << S.substr(X3,A3-X3) << " ";
            cerr << endl;
            for(int i:range(lb,ub)){
                cerr << sa[i] << " " <<S.substr(sa[i]) << " ";
                if(id[ql]<i){
                    cerr << rmq.get(i,id[ql]+1) <<endl;
                }
                else{
                    cerr << rmq.get(id[ql],i+1) <<endl;
                }
            }
            }
            */
            return true;
        }
        else{
            return false;
        }
        return A3-X3>=1;
    }
}





inline int solve(int ql,int qr,RMQ_PTR&rmq){
    int lb=0;
    int ub=qr-ql;
    while(ub-lb>1){
        const int mid = (lb+ub)/2;
        if(check(ql,qr,mid,rmq)){
            lb=mid;
        }
        else{
            ub=mid;
        }
    }
    if(lb>0)check(ql,qr,lb,rmq,true);
    return lb;
}




int main(){
    scanf("%d%d%s",&N,&Q,buf);
    reverse(buf,buf+N);
    S=buf;
    sa=SA::construct_sa(S);
    id=vector<int>(N+1);
    for(int i:range(N+1)){
        id[sa[i]]=i;
    }
    lcp=SA::construct_lcp(S,sa);
    RMQ_PTR rmq(lcp.begin(),lcp.end());
    st = SegmentTree<node,int>(N+1,segment_min, merge_segment, set_segment, set_leaf);
    DEBUG cerr << S << endl;


    for(int i:range(Q)){
        int l,r;
        scanf("%d%d",&l,&r);


        ret[i]=solve(N-r,N-l+1,rmq);
    }
    for(int i:range(Q)){
        printf("%d\n",ret[i]);
    }
    return 0;
}
accuracy: 1
time_ms: 5580
memory_kb: 57728
final_rank: 15

code_snippet: //
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#ifdef BTK
#define DEBUG if(1)
#else
#define CIN_ONLY if(1)
#define DEBUG if(0)
#endif
#define ALL(v) (v).begin(),(v).end()
#define REC(ret, ...) std::function<ret (__VA_ARGS__)>
template <typename T>inline bool chmin(T &l, T r){bool a = l>r; if (a)l = r; return a;}
template <typename T>inline bool chmax(T &l, T r){bool a = l<r; if (a)l = r; return a;}
template <typename T>istream& operator>>(istream &is, vector<T> &v){for (auto &it : v)is >> it;return is;}
class reverse_range {private:struct I {int x;int operator*() {return x-1;}bool operator!=(I& lhs) {return x>lhs.x;}void operator++() {--x;}};I i, n;public:reverse_range(int n) :i({ 0 }), n({ n }){}reverse_range(int i, int n) :i({ i }), n({ n }){}I& begin() {return n;}I& end() {return i;}};
class range {private: struct I { int x; int operator*() { return x; }bool operator!=(I& lhs) { return x<lhs.x; }void operator++() { ++x; } }; I i, n;public:range(int n) :i({ 0 }), n({ n }) {}range(int i, int n) :i({ i }), n({ n }) {}I& begin() { return i; }I& end() { return n; }reverse_range operator!(){return reverse_range(*i,*n);}};

int N,Q;
char buf[212345];
string S;


/*
  sa[0]=n //空文字列
  lcp[i]:=suffix[sa[i]]とsuffix[sa[i+1]]の共通高さ
 */

namespace latte{
    void create_begin_bucket(vector<int>&v,vector<int>&bucket){
        fill(bucket.begin(),bucket.end(),0);
        for(int i=0;i<(int)v.size();i++)bucket[v[i]]++;
        int sum=0;
        for(int i=0;i<(int)bucket.size();i++){bucket[i]+=sum;swap(sum,bucket[i]);}
    }

    void create_end_bucket(vector<int>&v,vector<int>&bucket){
        fill(bucket.begin(),bucket.end(),0);
        for(int i=0;i<(int)v.size();i++)bucket[v[i]]++;
        for(int i=1;i<(int)bucket.size();i++)bucket[i]+=bucket[i-1];
    }

    void induced_sort(vector<int>&v,vector<int>&sa,int mv,vector<int>&bucket,vector<int>&is_l){
        create_begin_bucket(v,bucket);
        for(int i=0;i<(int)v.size();i++)if(sa[i]>0&&is_l[sa[i]-1])sa[bucket[v[sa[i]-1]]++]=sa[i]-1;
    }

    void invert_induced_sort(vector<int>&v,vector<int>&sa,int mv,vector<int>&bucket,vector<int>&is_l){
        create_end_bucket(v,bucket);
        for(int i=v.size()-1;i>=0;i--)if(sa[i]>0&&!is_l[sa[i]-1])sa[--bucket[v[sa[i]-1]]]=sa[i]-1;
    }

    vector<int>sa_is(vector<int>v,int mv){
        if(v.size()==1)return vector<int>(1,0);

        vector<int>is_l(v.size());
        vector<int>bucket(mv+1);
        vector<int>sa(v.size(),-1);
        auto is_lms=[&](int x)->bool{return x>0&&is_l[x-1]&&!is_l[x];};

        is_l[v.size()-1]=0;
        for(int i=v.size()-2;i>=0;i--)is_l[i]=v[i]>v[i+1]||(v[i]==v[i+1]&&is_l[i+1]);
        create_end_bucket(v,bucket);
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))sa[--bucket[v[i]]]=i;
        induced_sort(v,sa,mv,bucket,is_l);
        invert_induced_sort(v,sa,mv,bucket,is_l);

        int cur=0;
        vector<int>order(v.size());
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))order[i]=cur++;

        vector<int>next_v(cur);
        cur=-1;
        int prev=-1;
        for(int i=0;i<(int)v.size();i++){
            if(!is_lms(sa[i]))continue;
            bool diff=false;
            for(int d=0;d<v.size();d++){
                if(prev==-1||v[sa[i]+d]!=v[prev+d]||is_l[sa[i]+d]!=is_l[prev+d]){
                    diff=true;
                    break;
                }
                else if(d>0&&is_lms(sa[i]+d))break;
            }
            if(diff){cur++;prev=sa[i];}
            next_v[order[sa[i]]]=cur;
        }

        vector<int>re_order(next_v.size());
        for(int i=0;i<(int)v.size();i++)if(is_lms(i))re_order[order[i]]=i;
        vector<int>next_sa=sa_is(next_v,cur);
        create_end_bucket(v,bucket);
        for(int i=0;i<sa.size();i++)sa[i]=-1;
        for(int i=next_sa.size()-1;i>=0;i--)sa[--bucket[v[re_order[next_sa[i]]]]]=re_order[next_sa[i]];
        induced_sort(v,sa,mv,bucket,is_l);
        invert_induced_sort(v,sa,mv,bucket,is_l);
        return sa;
    }

    vector<int> sa_is(string &s){
        vector<int>v(s.size()+1);
        for(int i=0;i<(int)s.size();i++)v[i]=s[i];
        return sa_is(v,*max_element(v.begin(),v.end()));
    }

    
}
namespace SA {
    int n, k;
    int R[500000];
    int T[500000];
    bool compare_sa(int i, int j) {
        if (R[i] != R[j])return R[i] < R[j];
        else {
            int ri = i + k <= n ? R[i + k] : -1;
            int rj = j + k <= n ? R[j + k] : -1;
            return ri < rj;
        }
    }
    vector<int> construct_sa(string& S) {
        return latte::sa_is(S);
        n = S.size();
        vector<int> sa(n + 1);
        for (int i = 0; i <= n; i++) {
            sa[i] = i;
            R[i] = i < n ? S[i] : -1;
        }
 
        for (k = 1; k <= n; k *= 2) {
            sort(sa.begin(), sa.end(), compare_sa);
            T[sa[0]] = 0;
            for (int i = 1; i <= n; i++) 
                T[sa[i]] = T[sa[i - 1]] + (compare_sa(sa[i - 1], sa[i]) ? 1 : 0);
            for (int i = 0; i <= n; i++)
                R[i] = T[i];
        }
        return sa;
    }
    vector<int> construct_lcp(string& S, vector<int> &sa) {
        n = S.size();
        for (int i = 0; i <= n; i++)R[sa[i]] = i;
        int h = 0;
        vector<int> lcp(n + 1, 0);
        for (int i = 0; i < n; i++) {
            int j = sa[R[i] - 1];
            if (h > 0)h--;
            for (; j + h < n&&i + h < n; h++) {
                if (S[j + h] != S[i + h])break;
            }
            lcp[R[i] - 1] = h;
        }
        return lcp;
    }
}

int ret[212345];

vector<int> sa,lcp;
vector<int> id;


namespace StaticRMQ {
	/*
	default:: range minimum query
	*/

	template<typename T>
	using MergeFunction = function<T(T,T)>;

	template<typename T>
	MergeFunction<T>
		getMin = [](T l, T r) {
		return l < r ? l : r;
	};

    template<typename T>
	MergeFunction<T>
		getMax = [](T l, T r) {
		return l > r ? l : r;
	};


	template<typename T>
	class BufferManager {
	private:
		T* const mem;
		int ptr;
	public:
		BufferManager(T* buf):mem(buf) {
			ptr = 0;
		}
		T* get(int m) {
			ptr += m;
			return mem + ptr - m;
		}
		void reset() {
			ptr = 0;
		}
	};

	namespace Buffer{
		// if N<=10^6 : BufferSize is enough for 5 * N
		constexpr int BufferSize = 5 * 1123456;
		using NodeType = int;
		NodeType mem[BufferSize];
		BufferManager<NodeType> buffer(mem);
	}

	template<typename T, typename ITR>
	struct SparseTable {
		const int n;
		const int logn;
		T* const mem;
		MergeFunction<T> Merge;
		void build() {
			for (int h = 0; h + 1 < logn; h++) {
				const int half = 1 << h;
				const int len = half << 1;
				for (int i = 0; i + len <= n; i++) {
					mem[h*n + n + i] = Merge(mem[h*n + i], mem[h*n + i + half]);
				}
			}
		}
		static inline int get_log(const int n) {
			int l = 1;
			for (int tmp = 1; tmp < n; tmp <<= 1)l++;
			return l;
		}
		SparseTable(ITR bg, ITR ed,
            MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
            )
			: n(distance(bg,ed)),
			logn(get_log(n)),
			mem(bf.get(n*logn)),
			Merge(f) {
			for (auto p = mem; bg != ed; ++bg, ++p) {
				(*p) = (*bg);
			}
			build();
		}
		inline int get_msb(const int size) {
#ifdef BTK
			int id = 0;
			for (int i = 0; i < logn; i++)if (size >> i)id = i;
			return id;
#else
			return 31 - __builtin_clz(size);
#endif
		}
		inline T get(const int l, const int r) {
			const int msb = get_msb(r - l);
			return Merge(mem[msb*n + l], mem[msb*n + r - (1 << msb)]);
		}
	};

	template<typename T, typename ITR>
	struct SmallRMQ{
		const int n;
		T* const mem;
		MergeFunction<T> Merge;
		void build(ITR bg,ITR ed) {
			int x = distance(bg, ed);
			for (auto p = mem; x > 0; x -= 4) {
				T tmp[4];
				tmp[0] = (*bg); ++bg;
				if (x >= 1)tmp[1] = (*bg); ++bg;
				if (x >= 2)tmp[2] = (*bg); ++bg;
				if (x >= 3)tmp[3] = (*bg); ++bg;
				for (int i = 0; i < 4; i++) {
					T ret = tmp[i];
					(*p) = ret; ++p;
					for (int j = i + 1; j < 4; j++) {
						ret = Merge(ret,tmp[j]);
						(*p) = ret; ++p;
					}
				}
			}
		}
		SmallRMQ(ITR bg, ITR ed,
			MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
            )
            : n(((distance(bg, ed)+3)>>2)<<2),
			mem(bf.get((n>>2) * 10)),
			Merge(f) {
			build(bg, ed);
		}
		inline T get(int l, int r) {
			const int block = l >> 2;
			l &= 3; r--; r &= 3;
			const int segment = (10 - (((4 - l)*(4 - l + 1)) >> 1)) + (r - l);
			return mem[block * 10 + segment];
		}

	};
	template<typename T, typename ITR>
	struct RMQ {
		const int n;
		MergeFunction<T> Merge;
		T* const workspace;
		SmallRMQ<T, ITR> small;
		SmallRMQ<T, T*> medium;
		SparseTable<T, T*> large;
		template<typename S, typename Itr>
		inline S buildBlock(Itr bg, Itr ed, BufferManager<T>& bf, const int size) {
			T* p = workspace;
			for (int x = distance(bg, ed); x > 0; ++p, x-=4) {
				(*p) = (*bg); ++bg;
				if(x>=1)(*p) = Merge(*p, *bg); ++bg;
				if(x>=2)(*p) = Merge(*p, *bg); ++bg;
				if(x>=3)(*p) = Merge(*p, *bg); ++bg;
			}
			return S(workspace, workspace+size, Merge, bf);
		}
		RMQ(ITR bg, ITR ed,
			MergeFunction<T> f = getMin<T>,
			BufferManager<T> &bf = Buffer::buffer
        ): n(((distance(bg, ed) + 15) >> 4) << 4),
			Merge(f),
			workspace(bf.get(n >> 2)),
			small(bg, ed, f, bf),
			medium(buildBlock<SmallRMQ<T, T*>, ITR>(bg, ed, bf, n >> 2)),
			large(buildBlock<SparseTable<T, T*>, T*>(workspace, workspace + (n >> 2), bf, n >> 4))
		{}
		inline T get(int l, int r) {
			int nl = (l >> 2) + 1;
			int nr = ((r - 1) >> 2);
			T ret = small.get(l, min(nl << 2, r));
			ret = Merge(ret, small.get(max(l, nr << 2),r));
			l = nl; r = nr;
			nl = (l >> 2) + 1; 
			nr = ((r - 1) >> 2);
			if (l >= r)return ret;
			ret = Merge(ret, medium.get(l, min(nl << 2, r)));
			ret = Merge(ret, medium.get(max(l, nr << 2), r));
			l = nl; r = nr;
			if (l >= r)return ret;
			else return Merge(ret, large.get(l, r));
		}
	};
}
using RMQ_PTR=StaticRMQ::RMQ<int,decltype(lcp.begin())>;


inline pair<int,int> get_range(int pos,int len,RMQ_PTR&rmq){
    int lb=pos;
    int ub=pos+1;
    {
        int ok=pos;
        int ng=-1;
        while(abs(ok-ng)>1){
            const int mid=(ok+ng)/2;
            if(rmq.get(mid,pos)>=len){
                ok=mid;
            }
            else{
                ng=mid;
            }
        }
        chmin(lb,ok);
    }
    {
        int ok=pos;
        int ng=N+1;
        while(abs(ok-ng)>1){
            const int mid=(ok+ng)/2;
            if(rmq.get(pos,mid)>=len){
                ok=mid;
            }
            else{
                ng=mid;
            }
        }
        chmax(ub,ng);
    }
    return {lb,ub};
}


constexpr int INF = 1e7;
template<typename SEG> using SetInitialLeaf = function<void(SEG&, int)>;
template<typename SEG> using SetInitialSegment = function<void(SEG&, const int, const int)>;
template<typename RET, typename SEG> using GetSingleSegment = function<RET(SEG&, const int, const int)>;
template<typename RET> using GetMergedSegment = function<RET(RET, RET)>;
template<typename SEG, typename Q> using UpdateSingleSegment = function<void(SEG&, Q)>;
template<typename SEG> using LazyUpdate = function<void(SEG&, SEG&, SEG&)>;

struct node { vector<int> x; };

int query_lb;

SetInitialLeaf<node>
set_leaf = [](node &v, const int id) {
};

SetInitialSegment<node>
set_segment = [](node &v, const int l, const int r) {
    const int lb = l;
    const int ub = min(N+1,r);
    v.x.reserve(max(ub-lb,0)+1);
    for(int i:range(lb,ub)){
        v.x.push_back(sa[i]);
    }
    v.x.push_back(INF);
    sort(ALL(v.x));
};

GetSingleSegment<int, node>
segment_min = [](node &v, const int l, const int r) {
	return *lower_bound(ALL(v.x),query_lb);
};

GetMergedSegment<int>
merge_segment = [](int l, int r) {
	return l < r ? l : r;
};


#ifndef NDBUF
#define NDBUF
template<typename T>
struct BufferManager {
	T *mem;
	int ptr;
	BufferManager(T* mem) {
		ptr = 0;
		this->mem = mem;
	}
	T* get(int m) {
		ptr += m;
		return mem + ptr - m;
	}
	void reset() {
		ptr = 0;
	}
};


#endif
namespace H {
	constexpr int BufferSize = 812345;
	using NodeType = node;
	NodeType mem[BufferSize];
	BufferManager<NodeType> buffer(mem);
}
template<typename Node, typename RET>
struct SegmentTree {
	int size;
	Node *seg;
	GetSingleSegment<RET, Node> get_single_segment;
	GetMergedSegment<RET> get_merged_segment;
	LazyUpdate<Node> lazy_update;
	void init(int l, int r, SetInitialSegment<Node>& init_segment, SetInitialLeaf<Node>& init_leaf, int k = 0) {
		auto &v = seg[k];
		init_segment(v, l, r);
		if (r - l == 1) {
			//葉の時の処理
			init_leaf(v, l);
		}
		else if (r - l>1) {
			int m = (l + r) / 2;
			init(l, m, init_segment, init_leaf, k * 2 + 1);
			init(m, r, init_segment, init_leaf, k * 2 + 2);
		}
	}
	SegmentTree(
		int n,
		GetSingleSegment<RET, Node> gss,
		GetMergedSegment<RET> gms,
		SetInitialSegment<Node> sis,
		SetInitialLeaf<Node> sil,
		LazyUpdate<Node> lu = [](Node &v,Node &l,Node &r){},
		BufferManager<Node>& buf = H::buffer
	)
		:get_single_segment(gss),
		get_merged_segment(gms),
		lazy_update(lu) {
		size = 1; while (size<n)size *= 2;
		seg = buf.get(size * 2 + 10);
		init(0, size, sis, sil);
	}
    SegmentTree(){
    }
#define LQ a,b,k*2+1,l,m
#define RQ a,b,k*2+2,m,r
	RET get(int a, int b, int k, int l, int r) {
		if (a <= l && r <= b)return get_single_segment(seg[k], l, r);
		if (r - l > 1)lazy_update(seg[k], seg[2 * k + 1], seg[2 * k + 2]);
		int m = (l + r) / 2;
		bool ll = !(m <= a || b <= l);
		bool rr = !(r <= a || b <= m);
		RET ret;
		if (ll&&rr)ret = get_merged_segment(get(LQ), get(RQ));
		else if (ll)ret = get(LQ); else ret = get(RQ);
		
		return ret;
	}
	RET get(int a, int b) {
		return get(a, b, 0, 0, size);
	}
	template<typename Q>
	void update(int a, int b, int k, int l, int r, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		if (r <= a || b <= l)return;
		if (a <= l && r <= b) {
			update_segment(seg[k], value);
		}
		else {
			if (r - l > 1)lazy_update(seg[k], seg[2 * k + 1], seg[2 * k + 2]);
			int m = (l + r) / 2;
			update(LQ, update_segment, value);
			update(RQ, update_segment, value);
			seg[k].ret = get_merged_segment(
				get_single_segment(seg[k * 2 + 1], l, m),
				get_single_segment(seg[k * 2 + 2], m, r)
			);
		}
	}
	template<typename Q>
	void update_segment(int a, int b, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		update(a, b, 0, 0, size, update_segment, value);
	}

	template<typename Q>
	void update_element(int a, UpdateSingleSegment<Node, Q> &update_segment, Q value) {
		update(a, a + 1, 0, 0, size, update_segment, value);
	}
};

SegmentTree<node,int> st;



inline bool check(int ql,int qr,int X_len,RMQ_PTR&rmq,bool db=false){
    if(X_len*3+3>qr-ql)return false;
    int lb,ub;
    tie(lb,ub)=get_range(id[ql],X_len,rmq);
    int p;
    {
        int X1=ql+X_len;
        query_lb = X1+1;
        p = st.get(lb,ub);
        int A1=p;
        int X2=p+X_len;
        if(X2>=N)return false;
        query_lb = X2+1;
        p = st.get(lb,ub);
        int A2=p;
        int X3=p+X_len;
        if(X3>=N)return false;
        int A3=qr;
        if(A3-X3>=1){
            /*
            if(db){
            cerr << S.substr(ql,X1-ql) << " ";
            cerr << S.substr(X1,A1-X1) << " ";
            cerr << S.substr(A1,X2-A1) << " ";
            cerr << S.substr(X2,A2-X2) << " ";
            cerr << S.substr(A2,X3-A2) << " ";
            cerr << S.substr(X3,A3-X3) << " ";
            cerr << endl;
            for(int i:range(lb,ub)){
                cerr << sa[i] << " " <<S.substr(sa[i]) << " ";
                if(id[ql]<i){
                    cerr << rmq.get(i,id[ql]+1) <<endl;
                }
                else{
                    cerr << rmq.get(id[ql],i+1) <<endl;
                }
            }
            }
            */
            return true;
        }
        else{
            return false;
        }
        return A3-X3>=1;
    }
}





inline int solve(int ql,int qr,RMQ_PTR&rmq){
    int lb=0;
    int ub=qr-ql;
    while(ub-lb>1){
        const int mid = (lb+ub)/2;
        if(check(ql,qr,mid,rmq)){
            lb=mid;
        }
        else{
            ub=mid;
        }
    }
    if(lb>0)check(ql,qr,lb,rmq,true);
    return lb;
}




int main(){
    scanf("%d%d%s",&N,&Q,buf);
    reverse(buf,buf+N);
    S=buf;
    sa=SA::construct_sa(S);
    id=vector<int>(N+1);
    for(int i:range(N+1)){
        id[sa[i]]=i;
    }
    lcp=SA::construct_lcp(S,sa);
    RMQ_PTR rmq(lcp.begin(),lcp.end());
    st = SegmentTree<node,int>(N+1,segment_min, merge_segment, set_segment, set_leaf);
    DEBUG cerr << S << endl;

    DEBUG {
        for(int i:range(N)){
            cerr << sa[i] <<" "<<S.substr(sa[i])<<" "<<lcp[i]<<endl;
        }
    }
    for(int i:range(Q)){
        int l,r;
        scanf("%d%d",&l,&r);
        DEBUG {
            for(int j:range(N-r,N-l+1)){
                cerr << S[j];
            }
            cerr << endl;
        }

        ret[i]=solve(N-r,N-l+1,rmq);
    }
    for(int i:range(Q)){
        printf("%d\n",ret[i]);
    }
    return 0;
}
accuracy: 1
time_ms: 5300
memory_kb: 57728
final_rank: 13

code_snippet: #include <bits/stdc++.h>

using namespace std;
using ll = long long;
using ull = unsigned long long;
template <class T> using V = vector<T>;
template <class T> using VV = V<V<T>>;
constexpr ll TEN(int n) { return (n == 0) ? 1 : 10 * TEN(n - 1); }

//#define int ll

#define FOR(i,a,b) for(int i=int(a);i<int(b);i++)
#define REP(i,b) FOR(i,0,b)

ll read(){
	ll x;
	scanf("%lld",&x);
	return x;
}

void print(ll x){
	printf("%lld\n",x);
}

string readString(){
	static char buf[1000010];
	scanf("%s",buf);
	return string(buf);
}

using vi=vector<int>;
#define PB push_back
#define ALL(x) x.begin(),x.end()

template<class T>
void chmin(T&a,T b){
	if(a>b)a=b;
}
template<class T>
void chmax(T&a,T b){
	if(a<b)a=b;
}

template<class T>
T Sq(T t){
	return t*t;
}

template<class Str> struct SA{
	Str s;
	vi sa,rsa,lcp;
	SA(){}
	SA(Str _s,vi _sa):s(_s),sa(_sa){
		int n=s.size();
		rsa=vi(n+1);
		REP(i,n+1)
			rsa[sa[i]]=i;
		lcp=vi(n);
		int h=0;
		REP(i,n){
			int j=sa[rsa[i]-1];
			if(h>0)h--;
			for(;j+h<n&&i+h<n;h++)
				if(s[j+h]!=s[i+h])
					break;
			lcp[rsa[i]-1]=h;
		}
	}
};

template<class Str> vi doublingSA(Str s){
	int n=s.size();
	vi sa(n+1);
	vi rsa(n+1);
	iota(ALL(sa),0);
	REP(i,n+1){
		rsa[i]=i<n?s[i]:-1;
	}
	vi tmp(n+1);
	for(int k=1;k<=n;k*=2){
		auto cmp=[&](int x,int y){
			if(rsa[x]!=rsa[y]) return rsa[x]<rsa[y];
			int rx=x+k<=n?rsa[x+k]:-1;
			int ry=y+k<=n?rsa[y+k]:-1;
			return rx<ry;
		};
		sort(ALL(sa),cmp);
		tmp[sa[0]]=0;
		FOR(i,1,n+1){
			tmp[sa[i]]=tmp[sa[i-1]]+cmp(sa[i-1],sa[i]);
		}
		copy(ALL(tmp),rsa.begin());
	}
	return sa;
}

int popcount(uint x){return __builtin_popcount(x);}
int bsr(uint x){return 31-__builtin_clz(x);}
int bsf(uint x){return __builtin_ctz(x);}

const int inf=INT_MAX/2-100;

struct SparseTable{
	vector<vi> data;
	SparseTable(vi v){
		int n=v.size();
		if(n==0)return;
		int lg=bsr(n)+1;
		data.resize(lg);
		data[0]=v;
		int l=1;
		FOR(s,1,lg){
			data[s]=vi(n);
			REP(i,n-l)
				data[s][i]=min(data[s-1][i],data[s-1][i+l]);
			l<<=1;
		}
	}
	int Query(int l,int r){
		if(r<=l)return inf;
		int u=bsr(r-l);
		return min(data[u][l],data[u][r-(1<<u)]);
	}
};

struct SegTree{
	int s;
	vi buf;
	SegTree(int n){
		s=1;
		while(s<n)s*=2;
		buf.resize(s*2,inf);
	}
	void Update(int i,int v){
		i+=s;
		buf[i]=v;
		while(i>>=1){
			buf[i]=min(buf[i*2],buf[i*2+1]);
		}
	}
	int Get(int b,int e,int l,int r,int i){
		if(e<=l||r<=b)return inf;
		if(b<=l&&r<=e)return buf[i];
		return min(Get(b,e,l,(l+r)/2,i*2),Get(b,e,(l+r)/2,r,i*2+1));
	}
	int Get(int b,int e){
		assert(b<e);
		return Get(b,e,0,s,1);
	}
};

const int Qmax=200010;
int l[Qmax],r[Qmax],lw[Qmax],up[Qmax],mid[Qmax],bg[Qmax],ed[Qmax];
bool ok[Qmax];

const int Nmax=200010;
struct Query{
	int i,k;
};
vector<Query> qs[Nmax];

signed main(){
	int n=read(),q=read();
	string s=readString();
	reverse(ALL(s));
	auto sa=SA<string>(s,doublingSA(s));
	SparseTable st(sa.lcp);
	
	REP(i,q){
		l[i]=read()-1,r[i]=read();
		tie(l[i],r[i])=make_tuple(n-r[i],n-l[i]);
		up[i]=max(1,(r[i]-l[i])/3);
	}
	
	while(1){
		REP(i,n+1){
			qs[i]=vector<Query>();
		}
		REP(i,q){
			ok[i]=false;
		}
		bool has=false;
		REP(i,q){
			if(up[i]-lw[i]>1){
				mid[i]=(lw[i]+up[i])/2;
				qs[l[i]+mid[i]+1].PB(Query{i,1});
				int cur=sa.rsa[l[i]];
				int left,right;
				{
					int a=0,b=cur+1;
					while(b-a>1){
						int m=(a+b)/2;
						if(st.Query(cur-m,cur)>=mid[i])
							a=m;
						else
							b=m;
					}
					left=cur-a;
				}
				{
					int a=0,b=n-cur+1;
					while(b-a>1){
						int m=(a+b)/2;
						if(st.Query(cur,cur+m)>=mid[i])
							a=m;
						else
							b=m;
					}
					right=cur+a;
				}
				bg[i]=left;
				ed[i]=right+1;
				has=true;
			}else{
				mid[i]=up[i];
			}
		}
		if(!has)break;
		
		SegTree seg(n+1);
		REP(i,n+1){
			seg.Update(i,sa.sa[i]);
		}
		
		REP(i,n+1){
			for(auto w:qs[i]){
				if(w.k==3){
					ok[w.i]=i<=r[w.i];
				}else{
					int mn=seg.Get(bg[w.i],ed[w.i]);
					int nx=mn+mid[w.i]+1;
					if(nx<=n){
						qs[nx].PB(Query{w.i,w.k+1});
					}
				}
			}
			seg.Update(sa.rsa[i],inf);
		}
		
		REP(i,q){
			if(ok[i])
				lw[i]=mid[i];
			else
				up[i]=mid[i];
		}
	}
	REP(i,q)
		print(lw[i]);
}
accuracy: 1
time_ms: 5120
memory_kb: 39600
final_rank: 10

code_snippet: #include <bits/stdc++.h>

using namespace std;

template< typename Monoid >
struct PersistentSegmentTree {
  using F = function< Monoid(Monoid, Monoid) >;

  struct Node {
    Monoid data;
    Node *l, *r;

    Node(const Monoid &data) : data(data), l(nullptr), r(nullptr) {}
  };


  int sz;
  const F f;
  const Monoid M1;

  PersistentSegmentTree(const F f, const Monoid &M1) : f(f), M1(M1) {}

  Node *build(const vector< Monoid > &v) {
    sz = (int) v.size();
    return build(0, (int) v.size(), v);
  }

  Node *merge(Node *l, Node *r) {
    auto t = new Node(f(l->data, r->data));
    t->l = l;
    t->r = r;
    return t;
  }

  Node *build(int l, int r, const vector< Monoid > &v) {
    if(l + 1 >= r) return new Node(v[l]);
    return merge(build(l, (l + r) >> 1, v), build((l + r) >> 1, r, v));
  }

  Node *update(int a, const Monoid &x, Node *k, int l, int r) {
    if(r <= a || a + 1 <= l) {
      return k;
    } else if(a <= l && r <= a + 1) {
      return new Node(x);
    } else {
      return merge(update(a, x, k->l, l, (l + r) >> 1), update(a, x, k->r, (l + r) >> 1, r));
    }
  }

  Node *update(Node *t, int k, const Monoid &x) {
    return update(k, x, t, 0, sz);
  }

  Monoid query(int a, int b, Node *k, int l, int r) {
    if(r <= a || b <= l) {
      return M1;
    } else if(a <= l && r <= b) {
      return k->data;
    } else {
      return f(query(a, b, k->l, l, (l + r) >> 1),
               query(a, b, k->r, (l + r) >> 1, r));
    }
  }

  Monoid query(Node *t, int a, int b) {
    return query(a, b, t, 0, sz);
  }
};

struct SuffixArray {
  vector< int > SA;
  const string s;

  SuffixArray(const string &str) : s(str) {
    SA.resize(s.size());
    iota(begin(SA), end(SA), 0);
    sort(begin(SA), end(SA), [&](int a, int b) {
      return s[a] == s[b] ? a > b : s[a] < s[b];
    });
    vector< int > classes(s.size()), c(s.begin(), s.end()), cnt(s.size());
    for(int len = 1; len < s.size(); len <<= 1) {
      for(int i = 0; i < s.size(); i++) {
        if(i > 0 && c[SA[i - 1]] == c[SA[i]] && SA[i - 1] + len < s.size() && c[SA[i - 1] + len / 2] == c[SA[i] + len / 2]) {
          classes[SA[i]] = classes[SA[i - 1]];
        } else {
          classes[SA[i]] = i;
        }
      }
      iota(begin(cnt), end(cnt), 0);
      copy(begin(SA), end(SA), begin(c));
      for(int i = 0; i < s.size(); i++) {
        int s1 = c[i] - len;
        if(s1 >= 0) SA[cnt[classes[s1]]++] = s1;
      }
      classes.swap(c);
    }
  }

  int operator[](int k) const {
    return SA[k];
  }

  size_t size() const {
    return s.size();
  }

  bool lt_substr(const string &t, int si = 0, int ti = 0) {
    int sn = (int) s.size(), tn = (int) t.size();
    while(si < sn && ti < tn) {
      if(s[si] < t[ti]) return true;
      if(s[si] > t[ti]) return false;
      ++si, ++ti;
    }
    return si >= sn && ti < tn;
  }

  int lower_bound(const string &t) {
    int low = -1, high = (int) SA.size();
    while(high - low > 1) {
      int mid = (low + high) / 2;
      if(lt_substr(t, SA[mid])) low = mid;
      else high = mid;
    }
    return high;
  }

  pair< int, int > lower_upper_bound(string &t) {
    int idx = lower_bound(t);
    int low = idx - 1, high = (int) SA.size();
    t.back()++;
    while(high - low > 1) {
      int mid = (low + high) / 2;
      if(lt_substr(t, SA[mid])) low = mid;
      else high = mid;
    }
    t.back()--;
    return {idx, high};
  }

  void output() {
    for(int i = 0; i < size(); i++) {
      cout << i << ": " << s.substr(SA[i]) << endl;
    }
  }
};

struct LongestCommonPrefixArray {
  const SuffixArray &SA;
  vector< int > LCP, rank;

  LongestCommonPrefixArray(const SuffixArray &SA) : SA(SA), LCP(SA.size()) {
    rank.resize(SA.size());
    for(int i = 0; i < SA.size(); i++) {
      rank[SA[i]] = i;
    }
    for(int i = 0, h = 0; i < SA.size(); i++) {
      if(rank[i] + 1 < SA.size()) {
        for(int j = SA[rank[i] + 1]; max(i, j) + h < SA.size() && SA.s[i + h] == SA.s[j + h]; ++h);
        LCP[rank[i] + 1] = h;
        if(h > 0) --h;
      }
    }
  }

  int operator[](int k) const {
    return LCP[k];
  }

  size_t size() const {
    return LCP.size();
  }

  void output() {
    for(int i = 0; i < size(); i++) {
      cout << i << ": " << LCP[i] << " " << SA.s.substr(SA[i]) << endl;
    }
  }
};

template< typename T >
struct SparseTable {
  vector< vector< T > > st;
  vector< int > lookup;

  SparseTable(const vector< T > &v) {
    int b = 0;
    while((1 << b) <= v.size()) ++b;
    st.assign(b, vector< T >(1 << b));
    for(int i = 0; i < v.size(); i++) {
      st[0][i] = v[i];
    }
    for(int i = 1; i < b; i++) {
      for(int j = 0; j + (1 << i) <= (1 << b); j++) {
        st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
      }
    }
    lookup.resize(v.size() + 1);
    for(int i = 2; i < lookup.size(); i++) {
      lookup[i] = lookup[i >> 1] + 1;
    }
  }

  inline T rmq(int l, int r) {
    if(l >= r) return 1 << 30;
    int b = lookup[r - l];
    return min(st[b][l], st[b][r - (1 << b)]);
  }
};

const int INF = 1 << 30;

int main() {
  int N, Q;
  cin >> N >> Q;
  string S;
  cin >> S;
  reverse(begin(S), end(S));
  SuffixArray sa(S);
  LongestCommonPrefixArray lcp(sa);

  // lcp.output();
  vector< int > rev(N);
  for(int i = 0; i < N; i++) rev[sa[i]] = i;
  auto f = [](int a, int b) { return min(a, b); };
  using Seg = PersistentSegmentTree< int >;
  Seg seg(f, INF);
  vector< Seg::Node * > nodes;
  Seg::Node *root = seg.build(vector< int >(N, INF));
  nodes.emplace_back(root);
  for(int i = N - 1; i >= 0; i--) {
    root = seg.update(root, rev[i], i);
    nodes.emplace_back(root);
  }
  reverse(begin(nodes), end(nodes));
  SparseTable< int > ukunichia(lcp.LCP);

  for(int i = 0; i < Q; i++) {
    int L, R;
    cin >> L >> R;
    --L, --R;
    L = N - L - 1;
    R = N - R - 1;
    swap(L, R);
    ++R;
    auto check = [&](int v) {

      int low, high;
      {
        int ok = rev[L], ng = -1;
        while(ok - ng > 1) {
          int mid = (ok + ng) / 2;
          if(ukunichia.rmq(mid + 1, rev[L] + 1) >= v) ok = mid;
          else ng = mid;
        }
        low = ok;
      }

      {
        int ok = rev[L], ng = N + 1;
        while(ng - ok > 1) {
          int mid = (ok + ng) / 2;
          if(ukunichia.rmq(rev[L] + 1, mid) >= v) ok = mid;
          else ng = mid;
        }
        high = ok;
      }

      int qq = L;
      qq += v + 1;
      if(qq > R) return false;

      {
        int tap = seg.query(nodes[qq], low, high);
        if(tap >= INF) return false;
        qq = tap;
        qq += v + 1;
        if(qq > R) return false;
      }

      {
        int tap = seg.query(nodes[qq], low, high);
        if(tap >= INF) return false;
        qq = tap;

        qq += v + 1;
        if(qq > R) return false;
      }

      return qq <= R;
    };


    int ok = 0, ng = (R - L) / 3 + 1;
    while(ng - ok > 1) {
      int mid = (ok + ng) / 2;
      if(check(mid)) ok = mid;
      else ng = mid;
    }
    cout << ok << endl;
  }
}
accuracy: 1
time_ms: 2770
memory_kb: 156876
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
typedef pair<int,P> P1;
typedef pair<P,P> P2;
#define pu push
#define pb push_back
#define mp make_pair
#define eps 1e-7
#define INF 1000000000
#define mod 1000000007 
#define fi first
#define sc second
#define rep(i,x) for(int i=0;i<x;i++)
#define repn(i,x) for(int i=1;i<=x;i++)
#define SORT(x) sort(x.begin(),x.end())
#define ERASE(x) x.erase(unique(x.begin(),x.end()),x.end())
#define POSL(x,v) (lower_bound(x.begin(),x.end(),v)-x.begin())
#define POSU(x,v) (upper_bound(x.begin(),x.end(),v)-x.begin())

int n,q;
string s;

int NN,k;
int ran[400005];
int tmp[400005];
int sa[400005];

bool compare_sa(int i,int j)
{
	if(ran[i] != ran[j]) return ran[i] < ran[j];
	else
	{
		int ri = i+k<=NN ? ran[i+k]: -1;
		int rj = j+k<=NN ? ran[j+k]: -1;
		
		return ri < rj;
	}
}

void construct_sa(string S)
{
	NN = S.size();
	for(int i=0;i<=NN;i++)
	{
		sa[i] = i;
		ran[i] = i<NN?S[i]:-1;
	}
	
	for(k=1;k<=NN;k*=2)
	{
		sort(sa,sa+NN+1,compare_sa);
		
		tmp[sa[0]] = 0;
		for(int i=1;i<=NN;i++)
		{
			tmp[sa[i]] = tmp[sa[i-1]] + compare_sa(sa[i-1],sa[i]);
		}
		for(int i=0;i<=NN;i++)
		{
			ran[i] = tmp[i];
		}
	}
}
int lcp[400005];
void construct_lcp(string S)
{
	int n = S.size();
	for(int i=0;i<=n;i++) ran[sa[i]] = i;
	
	int h = 0;
	lcp[0] = 0;
	
	for(int i=0;i<n;i++)
	{
		int j = sa[ran[i]-1];
		
		if(h) h--;
		for(;j+h<n && i+h<n;h++)
		{
			if(S[j+h] != S[i+h]) break;
		}
		lcp[ran[i]-1] = h;
	}
}

int mn[200005][19];
int xx[200005];
void update(int k,int a){
	mn[k][0] = a;
}
int query(int a,int b,int k,int l,int r){
	int w = xx[b-a+1];
	return min(mn[a][w],mn[b-(1<<w)+1][w]);
}
vector<int>S[(1<<19)];
void updatee(int k,int a){
	k+=(1<<18)-1;
	S[k].pb(a);
}
void make(){
	for(int i=(1<<18)-2;i>=0;i--){
		S[i].resize(S[i*2+1].size()+S[i*2+2].size());
		merge(S[i*2+1].begin(),S[i*2+1].end(),S[i*2+2].begin(),S[i*2+2].end(),S[i].begin());
	}
}
int check(int a,int b,int k,int l,int r,int M){
	if(r<a||b<l||a>b) return INF;
	if(a<=l&&r<=b){
		int x = POSL(S[k],M);
		if(x == S[k].size()) return INF;
		else return S[k][x];
	}
	return min( check(a,b,k*2+1,l,(l+r)/2,M), check(a,b,k*2+2,(l+r)/2+1,r,M) );
}
int main(){
	scanf("%d%d",&n,&q);
	cin>>s; reverse(s.begin(),s.end()); //cout << s << endl;
	construct_sa(s);
	construct_lcp(s);
	rep(i,200005)rep(j,19)mn[i][j] = INF;
	for(int i=0;i<n;i++){
		update(i,lcp[i]);
	}
	for(int j=0;j<18;j++){
		for(int i=0;i<n;i++){
			if(i+(1<<j) < n) mn[i][j+1] = min(mn[i][j],mn[i+(1<<j)][j]);
			else mn[i][j+1] = mn[i][j];
		}
	}
	repn(u,200000){
		for(int j=18;j>=0;j--){
			if(u >= (1<<j)){
				xx[u] = j; break;
			}
		}
	}
	
	for(int i=0;i<=n;i++){
		updatee(i,sa[i]);
	}
	make();
	for(int i=0;i<q;i++){
		int L,R; scanf("%d%d",&L,&R);
		R = n-R;
		L = n-L;
		swap(L,R);
		int RR = R;
		int lb = 0, ub = (R-L+1)/3;
		while(ub-lb > 1){
			int mid = (lb+ub)/2;
			int cur = L;
			bool fail = 0;
			rep(i,2){
				//cur+mid+1 以上
				int R = ran[cur];
				int l = -1,r = R;
				if(lcp[R-1] < mid) l = R-1;
				while(r-l > 1){
					int mm = (l+r)/2;
					if(query(mm,R-1,0,0,(1<<18)-1) < mid){
						l = mm;
					}
					else{
						r = mm;
					}
				}
				int up = r; 
				
				l = R,r = n+1;
				if(lcp[R] < mid) r = R+1;
				while(r-l > 1){
					int mm = (l+r)/2;
					if(query(R,mm-1,0,0,(1<<18)-1) < mid){
						r = mm;
					}
					else{
						l = mm;
					}
				}
				int down = l;
				int ret = check(up,down,0,0,(1<<18)-1,cur+mid+1);
				if(ret > 1e8){
					fail = 1; break;
				}
				else{
					cur = ret;
					if(cur+mid-1 > RR-1){
					   fail = 1;
					   break;
					}
				}
			}
			if(cur+mid-1 > R-1) fail = 1;
			if(fail) ub = mid;
			else lb = mid;
		}
		printf("%d\n",lb);
	}
	return 0;
}
accuracy: 1
time_ms: 5860
memory_kb: 58408
final_rank: 18

code_snippet: #include<iomanip>
#include<limits>
#include<thread>
#include<utility>
#include<iostream>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<numeric>
#include<cassert>
#include<random>
#include<chrono>
#include<unordered_set>
#include<unordered_map>
#include<fstream>
#include<list>
#include<functional>
#include<bitset>
#include<complex>
#include<tuple>
using namespace std;
typedef unsigned long long int ull;
typedef long long int ll;
typedef pair<ll,ll> pll;
typedef pair<int,int> pi;
typedef pair<double,double> pd;
typedef pair<double,ll> pdl;
#define F first
#define S second
const ll E=1e18+7;
const ll MOD=1000000007;

static const ll High=18;
static const ll MAX_N=200001;

bool Matrix[High][MAX_N]; //下から上
pair<int,int> Rank[High][MAX_N]; //(0,1) [high][size] 自分より前
int Mid[High]; //0-origin

struct WaveletMatrix{
    const int high;
    const int size;
    
    WaveletMatrix(int high,int size,const vector<ll> &A):high(high),size(size){init(A);}
    
    void init(vector<ll> A){
        for(int i=high-1;i>=0;i--){
            pair<int,int> R={0,0};
            for(int t=0;t<size;t++){
                Matrix[i][t]=A[t]>>i&1;
                Rank[i][t]=R;
                if(Matrix[i][t]){R.S++;}
                else{R.F++;}
            }
            Rank[i][size]=R;
            vector<ll> N(size);
            int l=0;
            for(int t=0;t<size;t++){
                if((A[t]>>i&1)==0){N[l]=A[t]; l++;}
            }
            Mid[i]=l;
            for(int t=0;t<size;t++){
                if(A[t]>>i&1){N[l]=A[t]; l++;}
            }
            A=N;
        }
    }
    
    ll access(int n) const {return access(high-1,n);}
    
    ll access(int h,int n) const {
        if(h==0){return Matrix[h][n];}
        if(Matrix[h][n]){
            return (1LL<<h)|access(h-1,Mid[h]+Rank[h][n].S);
        }
        return access(h-1,Rank[h][n].F);
    }
    
    //[l,r)
    int rank(ll s,int l,int r){
        return rank(s,l,r,high-1);
    }
    
    //[l,r)
    int rank(ll s,int l,int r,int h){
        if(h==0){
            if(s&1){return Rank[h][r].S-Rank[h][l].S;}
            return Rank[h][r].F-Rank[h][l].F;
        }
        if(s>>h&1){
            return rank(s,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        }
        return rank(s,Rank[h][l].F,Rank[h][r].F,h-1);
    }
    
    //[l,r)
    ll range_min(int l,int r){return range_min(l,r,high-1);}
    
    //[l,r)
    ll range_min(int l,int r,int h){
        if(h==0){
            if(Rank[h][r].F-Rank[h][l].F==0){return 1;}
            return 0;
        }
        if(Rank[h][r].F-Rank[h][l].F==0){
            return (1LL<<h)|range_min(Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        }
        return range_min(Rank[h][l].F,Rank[h][r].F,h-1);
    }
    
    //[l,r)
    inline ll range_max(int l,int r){return range_max(l,r,high-1);}
    
    //[l,r)
    ll range_max(int l,int r,int h){
        if(h==0){
            if(Rank[h][r].S-Rank[h][l].S==0){return 0;}
            return 1;
        }
        if(Rank[h][r].S-Rank[h][l].S==0){
            return range_max(Rank[h][l].F,Rank[h][r].F,h-1);
        }
        return (1LL<<h)|range_max(Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
    }
    
    //[s,t) [l,r)
    int range_count(ll s,ll t,int l,int r){return range_count(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    int range_count(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return 0;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return r-l;}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return 0;}
        int ret=0;
        if(h==0){
            if(s<=w && w<t){ret+=Rank[h][r].F-Rank[h][l].F;}
            if(s<=w+1 && w+1<t){ret+=Rank[h][r].S-Rank[h][l].S;}
            return ret;
        }
        ret+=range_count(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        ret+=range_count(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        return ret;
    }
    
    //[s,t) [l,r)
    inline ll range_min(ll s,ll t,int l,int r){return range_min(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    ll range_min(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return -1;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return range_min(l,r,h);}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return -1;}
        ll ret=-1;
        if(h==0){
            if(s<=w && w<t && Rank[h][r].F-Rank[h][l].F>0){return 0;}
            if(s<=w+1 && w+1<t && Rank[h][r].S-Rank[h][l].S>0){return 1;}
            return -1;
        }
        ret=range_min(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        if(ret==-1){
            ret=range_min(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
            if(ret==-1){return ret;}
            ret|=1LL<<h;
        }
        return ret;
    }
    
    //[s,t) [l,r)
    inline ll range_max(ll s,ll t,int l,int r){return range_max(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    ll range_max(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return -1;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return range_max(l,r,h);}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return -1;}
        if(h==0){
            if(s<=w+1 && w+1<t && Rank[h][r].S-Rank[h][l].S>0){return 1;}
            if(s<=w && w<t && Rank[h][r].F-Rank[h][l].F>0){return 0;}
            return -1;
        }
        ll ret=-1;
        ret=range_max(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        if(ret==-1){
            ret=range_max(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        }
        else{ret|=1LL<<h;}
        return ret;
    }
};




struct node{
    int idx,num;
    node* next;
    
    inline bool operator < (const node &A) const {
        if(num!=A.num){return num<A.num;}
        return (next==NULL?-1:next->num)<(A.next==NULL?-1:A.next->num);
    }
    
    inline bool operator == (const node &A) const {
        return num==A.num && (next==NULL?-1:next->num)==(A.next==NULL?-1:A.next->num);
    }
};

ll n,q;
string s;


node A[High][MAX_N];
ll B[High][MAX_N];


inline bool same(ll len,ll h,ll idx,ll idx2){
    idx=A[High-1][idx].idx;
    idx2=A[High-1][idx2].idx;
    idx-=len;
    idx2-=len;
    return (idx<0 || idx>=n?-1:A[h][B[h][idx]].num)==(idx2<0 || idx2>=n?-1:A[h][B[h][idx2]].num);
}


int main(){
    cin.tie(0);
    ios_base::sync_with_stdio(0);
    cin>>n>>q;
    cin>>s;
    for(int t=0;t<n;t++){
        A[0][t].idx=t;
        A[0][t].num=s[t]-'a';
        A[0][t].next=NULL;
    }
    sort(&A[0][0],&A[0][n]);
    for(int t=0;t<n;t++){B[0][A[0][t].idx]=t;}
    for(int t=0;t<n;t++){A[0][t].next=(A[0][t].idx-1<0?NULL:&A[0][B[0][A[0][t].idx-1]]);}
    for(int i=1;i<High;i++){
        for(int t=0;t<n;t++){A[i][t]=A[i-1][t];}
        sort(&A[i][0],&A[i][n]);
        A[i][0].num=0;
        for(int t=0;t<n;t++){
            B[i][A[i][t].idx]=t;
            if(t>0 && A[i-1][B[i-1][A[i][t-1].idx]]==A[i][t]){A[i][t].num=A[i][t-1].num;}
            else if(t>0){A[i][t].num=A[i][t-1].num+1;}
        }
        for(int t=0;t<n;t++){
            A[i][t].next=(A[i][t].idx-(1LL<<i)<0?NULL:&A[i][B[i][A[i][t].idx-(1LL<<i)]]);
        }
    }
    vector<ll> a(n);
    for(int i=0;i<n;i++){
        a[i]=A[High-1][i].idx;
    }
    WaveletMatrix M(High,(int)n,a);
    while(q--){
        ll l,r;
        cin>>l>>r;
        l--; r--;
        ll h=High;
        ll len=0;
        ll L=-1,R=n;
        while((--h)>=0){
            ll test=len+(1LL<<h);
            ll r1=r-test;
            if(r1<0){continue;}
            ll left=L;
            ll k=High;
            while((--k)>=0){
                if(left+(1LL<<k)>=B[High-1][r]){continue;}
                if(!same(len,h,left+(1LL<<k),B[High-1][r])){left+=1LL<<k;}
            }
            k=High;
            ll right=R;
            while((--k)>=0){
                if(right-(1LL<<k)<=B[High-1][r]){continue;}
                if(!same(len,h,right-(1LL<<k),B[High-1][r])){right-=1LL<<k;}
            }
            ll r2=M.range_max(l,r1,(int)left+1,(int)right);
            r2-=test;
            if(r2<=0){continue;}
            r2=M.range_max(l,r2,(int)left+1,(int)right);
            if(r2-test>=l){
                len+=1LL<<h;
                L=left; R=right;
            }
        }
        cout<<len<<'\n';
    }
    
    
    
    
    
    return 0;
}
accuracy: 1
time_ms: 5620
memory_kb: 123772
final_rank: 20

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr8,pr7,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
#define prArr(a) {cerr<<(#a)<<"={";int i=0;for(auto t:(a))cerr<<(i++?", ":"")<<t;cerr<<"}"<<endl;}
using namespace std;
using Int = long long;
using _int = int;
using ll = long long;
using Double = long double;
const Int INF = (1LL<<60)+1e9; // ~ 1.15 * 1e18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
template<class T1, class T2> ostream& operator<<(ostream& o,pair<T1,T2> p){return o<<"("<<p.first<<","<<p.second<<")";}
template<class T1, class T2, class T3> ostream& operator<<(ostream& o,tuple<T1,T2,T3> t){
  return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
template<class T1, class T2> istream& operator>>(istream& i,pair<T1,T2> &p){return i>>p.first>>p.second;}
template<class T> ostream& operator<<(ostream& o,vector<T> a){Int i=0;for(T t:a)o<<(i++?" ":"")<<t;return o;}
template<class T> istream& operator>>(istream& i,vector<T> &a){for(T &t:a)i>>t;return i;}
//INSERT ABOVE HERE

class Seg{
public:
  int INF = 1e9;
  int n,n_;
  vector<vector<int> > dat;
  
  //初期化
  Seg(){n=-1;}
  Seg(int n_):n_(n_){
    n=1;
    while(n<n_)n*=2;
    dat.resize(2*n-1,{});
  }

  inline vector<int> merge(const vector<int> &A,const vector<int> &B){
    int n = A.size(), m = B.size();
    int a = 0, b = 0;
    vector<int> res;
    while(a < n || b < m){
      if(b >= m) res.push_back(A[a++]);
      else if(a >= n) res.push_back(B[b++]);
      else if(A[a] < B[b]) res.push_back(A[a++]);
      else res.push_back(B[b++]);
    }
    return res;
  }
  
  void update(int k,int a){
    k+=n-1;
    dat[k]=vector<int>(1, a);
  }
  
  void build(){
    int k = n-2;
    while(k){
      int l = k * 2 + 1;
      int r = k * 2 + 2;
      dat[k] = merge(dat[l], dat[r]);
      k--;
    }
    for(int i=0;i<(int)dat.size();i++) dat[i].push_back(INF);
  }

  int dfs(int a,int b, int x, int k,int l,int r){
    if(r<=a||b<=l) return INF;
    if(a<=l&&r<=b) {
      int idx = lower_bound(dat[k].begin(), dat[k].end(), x) - dat[k].begin();
      assert(idx < (int)dat[k].size());
      return dat[k][idx];
    }
    int vl = dfs(a,b,x,k*2+1,l,(l+r)/2);
    int vr = dfs(a,b,x,k*2+2,(l+r)/2,r);
    return min(vl, vr);
  }
  
  //[a,b)の最小値を求める
  int get(int a,int b, int x){
    assert(a <= b), assert(a <= n_ && b <= n_), assert(a >= 0 && a >= 0);
    int res = dfs(a,b,x,0,0,n);
    if(res == INF) return -1;
    return res;
  }
};


class RollingHash{
public:
  typedef unsigned long long ll;
  ll B;  //B: ハッシュ基数
  int n;
  string s;
  vector<ll> hash;
  vector<ll> k;

  RollingHash():n(-1){};
  RollingHash(string s,ll B = 1777771):
    B(B),n(s.size()),s(s),hash(n+1),k(n+1)
  {
    for(int i=0;i<n;i++) hash[i+1] = (hash[i] * B + s[i]) ;
    k[0] = 1;
    for(int i=1;i<=n;i++) k[i] = (k[i-1] * B) ;
  }

  ll get(int l,int r)const{ //[l, r)
    //assert(0<=l && r<=n && l<=r);
    return (mod + hash[r] - hash[l] * k[r-l]);
  }
  
  //hash[a]とB.hash[b]からみて何文字一致してるか。
  int count(int a, const RollingHash &B, int b){
    int L = 0, R = min( n - a, B.n - b ) + 1;
    while( L + 1 < R ){
      int M = ( L + R ) / 2;
      get( a, a + M ) != B.get( b, b + M ) ? R = M : L = M;
    }
    return L;
  }

};

class SA{
public:
  string S;
  int n;
  vector<int> sa;
  RollingHash rh;
  
  SA():n(-1){}
  SA(string S):S(S),n(S.size()),sa(n), rh(S){  
    vector<int> rnk(n);
    vector<int> tmp(n);
    for(int i=0;i<n;i++) sa[i]=i;
    for(int i=0;i<n;i++) rnk[i] = S[i];
    
    //k文字についてソートされているところから、2k文字でソート
    for(int Len=1;Len<=n;Len*=2){
      
      //(rnk[i],rnk[i+k])と(rnk[j],rnk[j+k])を比較
      auto compare_sa=[&](const int &i,const int &j){
        if(rnk[i]!=rnk[j])return rnk[i]<rnk[j];
        int ri=i+Len<n?rnk[i+Len]:-1;
        int rj=j+Len<n?rnk[j+Len]:-1;
        return ri<rj;
      };
      
      sort(sa.begin(),sa.end(),compare_sa);

      //いったんtmpに次のランクを計算して、rnkに代入
      for(int i=1;i<n;i++)
        tmp[sa[i]] = tmp[sa[i-1]] + compare_sa(sa[i-1],sa[i]);
      rnk = tmp;
    }
  }

  int lower_bound(string T){
    assert(n >= 0);
    int L=-1,R=S.length();
    while(L+1<R){
      int M = (L+R)/2;
      S.compare(sa[M],T.size(),T)<0? L = M:R = M;
    }
    return R;
  }
  
  int lower_bound(const RollingHash &rh2, int l,int r, int upper = 0){ //[l, r)
    auto compare = [&](int X){
      int idx = sa[X];
      int n = S.size() - idx;
      int m = r - l;
      int len = rh.count(idx, rh2, l);
      if(len >= m) return 0;
      if(len >= n) return -1;
      if(len >= m) return 1;
      assert(S[idx + len] != rh2.s[l + len]);
      return (S[idx + len] < rh2.s[l + len]) ? -1:1;
    };
    
    assert(n >= 0);
    int L=-1, R=S.length();
    while(L+1<R){
      int M = (L+R)/2;
      if(upper) compare(M)<=0? L = M:R = M;
      else compare(M)<0? L = M:R = M;
    }
    return R;
  }

  int upper_bound(string T){return lower_bound(T+char('z'+ 1));}
  int upper_bound(const RollingHash &rh2, int l,int r){return lower_bound(rh2, l, r, 1);}
  
  int count(string T){return upper_bound(T) - lower_bound(T);};
  int count(const RollingHash &rh2, int l,int r){
    return upper_bound(rh2, l, r) - lower_bound(rh2, l, r);
  }
};

//AXBXCX
//XCXBXA

signed main(){
  srand((unsigned)time(NULL));
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);

  int N, Q;
  cin>>N>>Q;
  string str;
  cin>>str;
  reverse(str.begin(), str.end());
  
  SA sa(str);
  Seg seg(N);
  //pr(str);
  //pr(sa.sa);
  for(int i=0;i<N;i++) seg.update(i, sa.sa[i]);
  seg.build();

  vector<int> inv(N);
  for(int i=0;i<N;i++) inv[ sa.sa[i] ] = i;
  
  while(Q--){
    int l, r;
    cin>>l>>r;
    l = N - l;
    r = N - r;
    swap(l, r);
    
    auto check = [&](int X){
      if(l + X > N) return 0;
      int ll;
      {
	int L = 0, R = N;
	while(L + 1 < R){
	  int M = (L + R) / 2;
	  if(inv[l] - M < 0) {R = M; continue;}
	  int idx = sa.sa[inv[l] - M];
	  if(idx + X > N) {R = M; continue;}
	  Int hash1 = sa.rh.get(l, l + X);
	  Int hash2 = sa.rh.get(idx, idx + X);
	  hash1 != hash2? R=M:L=M;
	}
	ll = inv[l] - L;
      }
      
      int rr;
      {
	int L = 0, R = N;
	while(L + 1 < R){
	  int M = (L + R) / 2;
	  if(inv[l] + M >= N) {R = M; continue;}
	  int idx = sa.sa[inv[l] + M];
	  if(idx + X > N) {R = M; continue;}
	  Int hash1 = sa.rh.get(l, l + X);
	  Int hash2 = sa.rh.get(idx, idx + X);
	  hash1 != hash2? R=M:L=M;
	}
	rr = inv[l] + R;
      }
     
      
      //int ll = sa.lower_bound(sa.rh, l, l + X);
      //int rr = sa.upper_bound(sa.rh, l, l + X);
      int a = seg.get(ll, rr, l + X + 1);
      if(a == -1) return 0;
      int b = seg.get(ll, rr, a + X + 1);
      if(b == -1) return 0;
      if(b + X > r) return 0;
      return 1;
    };
    
    int L = 0, R = (r - l + 1 + 1) / 3;
    while(L+1 < R){
      int M = (L + R) / 2;
      check(M)? L = M:R = M;
    }
    cout<<L<<"\n";
  }

  return 0;
}
accuracy: 1
time_ms: 2540
memory_kb: 57932
final_rank: 5

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

struct FastIO{
  FastIO(){
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
}fastio_beet;


struct SuffixArray{
  int n;
  string S;
  vector<int> sa,lcp,rev;
  SuffixArray(){}
  SuffixArray(string &S):S(S){init();}
  void init(){
    n=S.length();
    S.push_back('$');
    build_sa();
  }
  void build_sa(){
    sa.assign(n+1,0);
    iota(sa.begin(),sa.end(),0);
    sort(sa.begin(),sa.end(),
         [&](int a,int b){
           if(S[a]==S[b]) return a>b;
           return S[a]<S[b];
         });
    vector<int> c(n+1,0),r(n+1),cnt(n+1),s(n+1);
    for(int i=0;i<=n;i++) r[i]=S[i];
    for(int len=1;len<=n;len*=2){
      for(int i=0;i<=n;i++){
        c[sa[i]]=
          i>0 &&
          r[sa[i-1]]==r[sa[i]] &&
          sa[i-1]+len<=n &&
          r[sa[i-1]+len/2]==r[sa[i]+len/2] ?
          c[sa[i-1]]:i;
      }
      iota(cnt.begin(),cnt.end(),0);
      copy(sa.begin(),sa.end(),r.begin());
      for(int i=0;i<=n;i++){
        int s1=r[i]-len;
        if(s1>=0) sa[cnt[c[s1]]++]=s1;
      }
      c.swap(r);
    }
  }
};


struct RollingHash{
  using ull = unsigned long long;
  vector<ull> hash,p;
  RollingHash(){}
  RollingHash(const string &s,ull B=1000000007LL){
    int n=s.size();
    hash.assign(n+1,0);
    p.assign(n+1,1);
    for(int i=0;i<n;i++){
      hash[i+1]=hash[i]*B+s[i];
      p[i+1]=p[i]*B;
    }
  }
  //S[l, r)
  ull find(int l,int r){
    return hash[r]-hash[l]*p[r-l];
  }
};


struct FullyIndexableDictionary{
  int len,blk;
  vector<unsigned> bit;
  vector<int> sum;
  
  FullyIndexableDictionary(){}
  FullyIndexableDictionary(int len)
    :len(len),blk((len+31)>>5),bit(blk,0),sum(blk,0){}
  
  void set(int k){
    bit[k>>5]|=1u<<(k&31);
  }

  void build(){
    sum[0]=0;
    for(int i=1;i<blk;i++)
      sum[i]=sum[i-1]+__builtin_popcount(bit[i-1]);
  }

  bool operator[](int k) const{
    return bool((bit[k>>5]>>(k&31))&1);
  }
  
  int rank(int k){
    return sum[k>>5]+__builtin_popcount(bit[k>>5]&((1u<<(k&31))-1));
  }
  
  int rank(bool v,int k){
    return (v?rank(k):k-rank(k));
  }

  int select(bool v,int k){
    if(k<0||rank(v,len)<=k) return -1;
    int low=0,high=len;
    while(low+1<high){
      int mid=(low+high)>>1;
      if(rank(v,mid)>=k+1) high=mid;
      else low=mid;
    }
    return high-1;
  }

  int select(bool v,int i,int l){
    return select(v,i+rank(v,l));
  }
};

template<class T,int MAXLOG>
struct WaveletMatrix{
  int len;
  FullyIndexableDictionary mat[MAXLOG];
  int zs[MAXLOG],buff1[MAXLOG],buff2[MAXLOG];
  static const T npos=-1;
  
  WaveletMatrix(vector<T> data){
    len=data.size();
    vector<T> l(len),r(len);
    for(int dep=0;dep<MAXLOG;dep++){
      mat[dep]=FullyIndexableDictionary(len+1);
      int p=0,q=0;
      for(int i=0;i<len;i++){
        bool k=(data[i]>>(MAXLOG-(dep+1)))&1;
        if(k) r[q++]=data[i],mat[dep].set(i);
        else  l[p++]=data[i];
      }
      zs[dep]=p;
      mat[dep].build();
      swap(l,data);
      for(int i=0;i<q;i++) data[p+i]=r[i];
    }
  }
  
  T access(int k){
    T res=0;
    bool bit;
    for(int dep=0;dep<MAXLOG;dep++){
      bit=mat[dep][k];
      res=(res<<1)|bit;
      k=mat[dep].rank(bit,k)+zs[dep]*dep;
    }
    return res;
  }

  // return the number of v in [0,k)
  int rank(T v,int k){
    int l=0,r=k;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return r-l;
  }

  // return the position of k-th v
  int select(T v,int k){
    rank(v,len);
    for(int dep=MAXLOG-1;dep>=0;dep--){
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      k=mat[dep].select(bit,k,buff1[dep]);
      if(k>=buff2[dep]||k<0) return -1;
      k-=buff1[dep];
    }
    return k;
  }

  int select(T v,int k,int l){
    return select(v,k+rank(v,l));
  }

  // return k-th largest value in [l,r)
  T quantile(int l,int r,int k){
    if(r-l<=k||k<0) return -1;
    T res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      int p=mat[dep].rank(1,l);
      int q=mat[dep].rank(1,r);
      if(q-p>k){
        l=p+zs[dep];
        r=q+zs[dep];
        res|=T(1)<<(MAXLOG-(dep+1));
      }else{
        k-=(q-p);
        l-=p;
        r-=q;
      }
    }
    return res;
  }
  
  T rquantile(int l,int r,int k){
    return quantile(l,r,r-l-k-1);
  }

  int le(int l,int r,T v){
    int res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      if(bit) res+=r-l+mat[dep].rank(bit,l)-mat[dep].rank(bit,r);
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return res+(r-l);    
  }
  
  T succ(int l,int r,T v){
    int k=le(l,r,v);
    return k==r-l?npos:rquantile(l,r,k);
  }
};


//INSERT ABOVE HERE
signed main(){
  int n,q;
  cin>>n>>q;
  string s;
  cin>>s;
  reverse(s.begin(),s.end());

  SuffixArray sa(s);
  vector<int> rev(n);
  auto vs=sa.sa;
  vs.erase(vs.begin());
  for(int i=0;i<n;i++){
    rev[vs[i]]=i;
  }
  using WM = WaveletMatrix<int, 18>;
  WM wm(vs);

  RollingHash rh(s);
  auto calc=
    [&](int a,int b)->int{      
      int pos=rev[a];
      auto check=
        [&](int x)->int{
          auto hs=rh.find(a,a+x);
          int ll=-1,rr=-1;
          {
            int l=-1,r=pos;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&rh.find(vs[m],vs[m]+x)==hs) r=m;
              else l=m;
            }
            ll=r;
          }
          {
            int l=pos,r=n;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&rh.find(vs[m],vs[m]+x)==hs) l=m;
              else r=m;
            }
            rr=r;
          }
          // [ll, rr)
          int p=wm.succ(ll,rr,a+x);
          if(p==WM::npos) return 0;
          int q=wm.succ(ll,rr,p+x);
          if(q==WM::npos) return 0;
          return q+x<b;
        };
      
      int l=0,r=(b-a+2)/3;
      while(l+1<r){
        int m=(l+r)>>1;
        if(check(m)) l=m;
        else r=m;
      }
      return l;
    };
    
  for(int i=0;i<q;i++){
    int a,b;
    cin>>a>>b;
    a--;
    swap(a,b);
    a=n-a;
    b=n-b;
    cout<<calc(a,b)<<"\n";
  }
  cout<<flush;
  return 0;
}
accuracy: 1
time_ms: 2680
memory_kb: 10364
final_rank: 2

code_snippet: #include<bits/stdc++.h>
using namespace std;
using Int = long long;
template<typename T1,typename T2> inline void chmin(T1 &a,T2 b){if(a>b) a=b;}
template<typename T1,typename T2> inline void chmax(T1 &a,T2 b){if(a<b) a=b;}

struct FastIO{
  FastIO(){
    cin.tie(0);
    ios::sync_with_stdio(0);
  }
}fastio_beet;


struct SuffixArray{
  int n;
  string S;
  vector<int> sa,lcp,rev;
  SuffixArray(){}
  SuffixArray(string &S):S(S){init();}
  void init(){
    n=S.length();
    S.push_back('$');
    build_sa();
  }
  void build_sa(){
    sa.assign(n+1,0);
    iota(sa.begin(),sa.end(),0);
    sort(sa.begin(),sa.end(),
         [&](int a,int b){
           if(S[a]==S[b]) return a>b;
           return S[a]<S[b];
         });
    vector<int> c(n+1,0),r(n+1),cnt(n+1),s(n+1);
    for(int i=0;i<=n;i++) r[i]=S[i];
    for(int len=1;len<=n;len*=2){
      for(int i=0;i<=n;i++){
        c[sa[i]]=
          i>0 &&
          r[sa[i-1]]==r[sa[i]] &&
          sa[i-1]+len<=n &&
          r[sa[i-1]+len/2]==r[sa[i]+len/2] ?
          c[sa[i-1]]:i;
      }
      iota(cnt.begin(),cnt.end(),0);
      copy(sa.begin(),sa.end(),r.begin());
      for(int i=0;i<=n;i++){
        int s1=r[i]-len;
        if(s1>=0) sa[cnt[c[s1]]++]=s1;
      }
      c.swap(r);
    }
  }
};


struct RollingHash{
  using ull = unsigned long long;
  vector<ull> hash,p;
  RollingHash(){}
  RollingHash(const string &s,ull B=1000000007LL){
    int n=s.size();
    hash.assign(n+1,0);
    p.assign(n+1,1);
    for(int i=0;i<n;i++){
      hash[i+1]=hash[i]*B+s[i];
      p[i+1]=p[i]*B;
    }
  }
  //S[l, r)
  ull find(int l,int r){
    return hash[r]-hash[l]*p[r-l];
  }
};


struct FullyIndexableDictionary{
  int len,blk;
  vector<unsigned> bit;
  vector<int> sum;
  
  FullyIndexableDictionary(){}
  FullyIndexableDictionary(int len)
    :len(len),blk((len+31)>>5),bit(blk,0),sum(blk,0){}
  
  void set(int k){
    bit[k>>5]|=1u<<(k&31);
  }

  void build(){
    sum[0]=0;
    for(int i=1;i<blk;i++)
      sum[i]=sum[i-1]+__builtin_popcount(bit[i-1]);
  }

  bool operator[](int k) const{
    return bool((bit[k>>5]>>(k&31))&1);
  }
  
  int rank(int k){
    return sum[k>>5]+__builtin_popcount(bit[k>>5]&((1u<<(k&31))-1));
  }
  
  int rank(bool v,int k){
    return (v?rank(k):k-rank(k));
  }

  int select(bool v,int k){
    if(k<0||rank(v,len)<=k) return -1;
    int low=0,high=len;
    while(low+1<high){
      int mid=(low+high)>>1;
      if(rank(v,mid)>=k+1) high=mid;
      else low=mid;
    }
    return high-1;
  }

  int select(bool v,int i,int l){
    return select(v,i+rank(v,l));
  }
};

template<class T,int MAXLOG>
struct WaveletMatrix{
  int len;
  FullyIndexableDictionary mat[MAXLOG];
  int zs[MAXLOG],buff1[MAXLOG],buff2[MAXLOG];
  static const T npos=-1;
  
  WaveletMatrix(vector<T> data){
    len=data.size();
    vector<T> l(len),r(len);
    for(int dep=0;dep<MAXLOG;dep++){
      mat[dep]=FullyIndexableDictionary(len+1);
      int p=0,q=0;
      for(int i=0;i<len;i++){
        bool k=(data[i]>>(MAXLOG-(dep+1)))&1;
        if(k) r[q++]=data[i],mat[dep].set(i);
        else  l[p++]=data[i];
      }
      zs[dep]=p;
      mat[dep].build();
      swap(l,data);
      for(int i=0;i<q;i++) data[p+i]=r[i];
    }
  }
  
  T access(int k){
    T res=0;
    bool bit;
    for(int dep=0;dep<MAXLOG;dep++){
      bit=mat[dep][k];
      res=(res<<1)|bit;
      k=mat[dep].rank(bit,k)+zs[dep]*dep;
    }
    return res;
  }

  // return the number of v in [0,k)
  int rank(T v,int k){
    int l=0,r=k;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return r-l;
  }

  // return the position of k-th v
  int select(T v,int k){
    rank(v,len);
    for(int dep=MAXLOG-1;dep>=0;dep--){
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      k=mat[dep].select(bit,k,buff1[dep]);
      if(k>=buff2[dep]||k<0) return -1;
      k-=buff1[dep];
    }
    return k;
  }

  int select(T v,int k,int l){
    return select(v,k+rank(v,l));
  }

  // return k-th largest value in [l,r)
  T quantile(int l,int r,int k){
    if(r-l<=k||k<0) return -1;
    T res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      int p=mat[dep].rank(1,l);
      int q=mat[dep].rank(1,r);
      if(q-p>k){
        l=p+zs[dep];
        r=q+zs[dep];
        res|=(1<<(MAXLOG-(dep+1)));
      }else{
        k-=(q-p);
        l-=p;
        r-=q;
      }
    }
    return res;
  }
  
  T rquantile(int l,int r,int k){
    return quantile(l,r,r-l-k-1);
  }
  
  pair<int, int> ll(int l,int r,T v){
    int res=0;
    for(int dep=0;dep<MAXLOG;dep++){
      buff1[dep]=l;buff2[dep]=r;
      bool bit=(v>>(MAXLOG-(dep+1)))&1;
      if(bit) res+=r-l+mat[dep].rank(bit,l)-mat[dep].rank(bit,r);
      l=mat[dep].rank(bit,l)+zs[dep]*bit;
      r=mat[dep].rank(bit,r)+zs[dep]*bit;
    }
    return make_pair(res,r-l);    
  }
  
  int lt(int l,int r,T v){
    auto p=ll(l,r,v);
    return p.first;
  }
  
  int le(int l,int r,T v){
    auto p=ll(l,r,v);
    return p.first+p.second;
  }
  
  T succ(int l,int r,T v){
    int k=le(l,r,v);
    return k==r-l?npos:rquantile(l,r,k);
  }

  T pred(int l,int r,T v){
    int k=lt(l,r,v);
    return k?rquantile(l,r,k-1):npos;
  }
};


//INSERT ABOVE HERE
signed main(){
  int n,q;
  cin>>n>>q;
  string s;
  cin>>s;

  SuffixArray sa(s);
  vector<int> rev(n);
  auto vs=sa.sa;
  vs.erase(vs.begin());
  for(int i=0;i<n;i++){
    rev[vs[i]]=i;
  }
  using WM = WaveletMatrix<int, 18>;
  WM wm(vs);

  RollingHash rh(s);
  auto calc=
    [&](int a,int b)->int{
      auto check=
        [&](int x)->int{
          int p=b-x;
          int pos=rev[p];
          int ll=-1,rr=-1;
          {
            int l=-1,r=pos;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&rh.find(vs[m],vs[m]+x)==rh.find(p,p+x)) r=m;
              else l=m;
            }
            ll=r;
          }
          {
            int l=pos,r=n;
            while(l+1<r){
              int m=(l+r)>>1;
              if(vs[m]+x<=n&&rh.find(vs[m],vs[m]+x)==rh.find(p,p+x)) l=m;
              else r=m;
            }
            rr=r;
          }
          // [ll, rr)
          int q=wm.pred(ll,rr,p-x);
          if(q==WM::npos||q-x<0) return 0;
          int k=wm.pred(ll,rr,q-x);
          if(k==WM::npos) return 0;
          return a<k;
        };
      
      int l=0,r=(b-a+2)/3;
      while(l+1<r){
        int m=(l+r)>>1;
        if(check(m)) l=m;
        else r=m;
      }
      return l;
    };
    
  for(int i=0;i<q;i++){
    int a,b;
    cin>>a>>b;
    a--;
    cout<<calc(a,b)<<"\n";
  }
  cout<<flush;
  return 0;
}
accuracy: 1
time_ms: 2940
memory_kb: 10364
final_rank: 3

code_snippet: #include <bits/stdc++.h>
#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME
#define pr(...) cerr<< GET_MACRO(__VA_ARGS__,pr8,pr7,pr6,pr5,pr4,pr3,pr2,pr1)(__VA_ARGS__) <<endl
#define pr1(a) (#a)<<"="<<(a)<<" "
#define pr2(a,b) pr1(a)<<pr1(b)
#define pr3(a,b,c) pr1(a)<<pr2(b,c)
#define pr4(a,b,c,d) pr1(a)<<pr3(b,c,d)
#define pr5(a,b,c,d,e) pr1(a)<<pr4(b,c,d,e)
#define pr6(a,b,c,d,e,f) pr1(a)<<pr5(b,c,d,e,f)
#define pr7(a,b,c,d,e,f,g) pr1(a)<<pr6(b,c,d,e,f,g)
#define pr8(a,b,c,d,e,f,g,h) pr1(a)<<pr7(b,c,d,e,f,g,h)
#define prArr(a) {cerr<<(#a)<<"={";int i=0;for(auto t:(a))cerr<<(i++?", ":"")<<t;cerr<<"}"<<endl;}
using namespace std;
using Int = long long;
using _int = int;
using ll = long long;
using Double = long double;
const Int INF = (1LL<<60)+1e9; // ~ 1.15 * 1e18
const Int mod = (1e9)+7;
const Double EPS = 1e-8;
const Double PI = 6.0 * asin((Double)0.5);
using P = pair<Int,Int>;
template<class T> T Max(T &a,T b){return a=max(a,b);}
template<class T> T Min(T &a,T b){return a=min(a,b);}
template<class T1, class T2> ostream& operator<<(ostream& o,pair<T1,T2> p){return o<<"("<<p.first<<","<<p.second<<")";}
template<class T1, class T2, class T3> ostream& operator<<(ostream& o,tuple<T1,T2,T3> t){
  return o<<"("<<get<0>(t)<<","<<get<1>(t)<<","<<get<2>(t)<<")";}
template<class T1, class T2> istream& operator>>(istream& i,pair<T1,T2> &p){return i>>p.first>>p.second;}
template<class T> ostream& operator<<(ostream& o,vector<T> a){Int i=0;for(T t:a)o<<(i++?" ":"")<<t;return o;}
template<class T> istream& operator>>(istream& i,vector<T> &a){for(T &t:a)i>>t;return i;}
//INSERT ABOVE HERE

class Seg{
public:
  int INF = 1e9;
  int n,n_;
  vector<vector<int> > dat;
  
  //初期化
  Seg(){n=-1;}
  Seg(int n_):n_(n_){
    n=1;
    while(n<n_)n*=2;
    dat.resize(2*n-1,{});
  }

  inline vector<int> merge(const vector<int> &A,const vector<int> &B){
    int n = A.size(), m = B.size();
    int a = 0, b = 0;
    vector<int> res;
    while(a < n || b < m){
      if(b >= m) res.push_back(A[a++]);
      else if(a >= n) res.push_back(B[b++]);
      else if(A[a] < B[b]) res.push_back(A[a++]);
      else res.push_back(B[b++]);
    }
    return res;
  }
  
  void update(int k,int a){
    k+=n-1;
    dat[k]=vector<int>(1, a);
  }
  
  void build(){
    int k = n-2;
    while(k){
      int l = k * 2 + 1;
      int r = k * 2 + 2;
      dat[k] = merge(dat[l], dat[r]);
      k--;
    }
    for(int i=0;i<(int)dat.size();i++) dat[i].push_back(INF);
  }

  int dfs(int a,int b, int x, int k,int l,int r){
    if(r<=a||b<=l) return INF;
    if(a<=l&&r<=b) {
      int idx = lower_bound(dat[k].begin(), dat[k].end(), x) - dat[k].begin();
      assert(idx < (int)dat[k].size());
      return dat[k][idx];
    }
    int vl = dfs(a,b,x,k*2+1,l,(l+r)/2);
    int vr = dfs(a,b,x,k*2+2,(l+r)/2,r);
    return min(vl, vr);
  }
  
  //[a,b)の最小値を求める
  int get(int a,int b, int x){
    assert(a <= b), assert(a <= n_ && b <= n_), assert(a >= 0 && a >= 0);
    int res = dfs(a,b,x,0,0,n);
    if(res == INF) return -1;
    return res;
  }
};


class RollingHash{
public:
  typedef unsigned long long ll;
  ll B;  //B: ハッシュ基数
  int n;
  string s;
  vector<ll> hash;
  vector<ll> k;

  RollingHash():n(-1){};
  RollingHash(string s,ll B = 1777771):
    B(B),n(s.size()),s(s),hash(n+1),k(n+1)
  {
    for(int i=0;i<n;i++) hash[i+1] = (hash[i] * B + s[i]) ;
    k[0] = 1;
    for(int i=1;i<=n;i++) k[i] = (k[i-1] * B) ;
  }

  ll get(int l,int r)const{ //[l, r)
    //assert(0<=l && r<=n && l<=r);
    return (mod + hash[r] - hash[l] * k[r-l]);
  }
  
  //hash[a]とB.hash[b]からみて何文字一致してるか。
  int count(int a, const RollingHash &B, int b){
    int L = 0, R = min( n - a, B.n - b ) + 1;
    while( L + 1 < R ){
      int M = ( L + R ) / 2;
      get( a, a + M ) != B.get( b, b + M ) ? R = M : L = M;
    }
    return L;
  }

};

class SA{
public:
  string S;
  int n;
  vector<int> sa;
  RollingHash rh;
  
  SA():n(-1){}
  SA(string S):S(S),n(S.size()),sa(n), rh(S){  
    vector<int> rnk(n);
    vector<int> tmp(n);
    for(int i=0;i<n;i++) sa[i]=i;
    for(int i=0;i<n;i++) rnk[i] = S[i];
    
    //k文字についてソートされているところから、2k文字でソート
    for(int Len=1;Len<=n;Len*=2){
      
      //(rnk[i],rnk[i+k])と(rnk[j],rnk[j+k])を比較
      auto compare_sa=[&](const int &i,const int &j){
        if(rnk[i]!=rnk[j])return rnk[i]<rnk[j];
        int ri=i+Len<n?rnk[i+Len]:-1;
        int rj=j+Len<n?rnk[j+Len]:-1;
        return ri<rj;
      };
      
      sort(sa.begin(),sa.end(),compare_sa);

      //いったんtmpに次のランクを計算して、rnkに代入
      for(int i=1;i<n;i++)
        tmp[sa[i]] = tmp[sa[i-1]] + compare_sa(sa[i-1],sa[i]);
      rnk = tmp;
    }
  }

  int lower_bound(string T){
    assert(n >= 0);
    int L=-1,R=S.length();
    while(L+1<R){
      int M = (L+R)/2;
      S.compare(sa[M],T.size(),T)<0? L = M:R = M;
    }
    return R;
  }
  
  int lower_bound(const RollingHash &rh2, int l,int r, int upper = 0){ //[l, r)
    auto compare = [&](int X){
      int idx = sa[X];
      int n = S.size() - idx;
      int m = r - l;
      int len = rh.count(idx, rh2, l);
      if(len >= m) return 0;
      if(len >= n) return -1;
      if(len >= m) return 1;
      assert(S[idx + len] != rh2.s[l + len]);
      return (S[idx + len] < rh2.s[l + len]) ? -1:1;
    };
    
    assert(n >= 0);
    int L=-1, R=S.length();
    while(L+1<R){
      int M = (L+R)/2;
      if(upper) compare(M)<=0? L = M:R = M;
      else compare(M)<0? L = M:R = M;
    }
    return R;
  }

  int upper_bound(string T){return lower_bound(T+char('z'+ 1));}
  int upper_bound(const RollingHash &rh2, int l,int r){return lower_bound(rh2, l, r, 1);}
  
  int count(string T){return upper_bound(T) - lower_bound(T);};
  int count(const RollingHash &rh2, int l,int r){
    return upper_bound(rh2, l, r) - lower_bound(rh2, l, r);
  }
};

//AXBXCX
//XCXBXA

signed main(){
  srand((unsigned)time(NULL));
  cin.tie(0);
  ios_base::sync_with_stdio(0);
  cout << fixed << setprecision(12);

  int N, Q;
  cin>>N>>Q;
  string str;
  cin>>str;
  reverse(str.begin(), str.end());
  
  SA sa(str);
  Seg seg(N);
  //pr(str);
  //pr(sa.sa);
  for(int i=0;i<N;i++) seg.update(i, sa.sa[i]);
  seg.build();

  vector<int> inv(N);
  for(int i=0;i<N;i++) inv[ sa.sa[i] ] = i;
  
  while(Q--){
    int l, r;
    cin>>l>>r;
    l = N - l;
    r = N - r;
    swap(l, r);
    
    auto check = [&](int X){
      if(l + X > N) return 0;
      int ll;
      {
	int L = 0, R = N;
	while(L + 1 < R){
	  int M = (L + R) / 2;
	  if(inv[l] - M < 0) {R = M; continue;}
	  int idx = sa.sa[inv[l] - M];
	  if(idx + X > N) {R = M; continue;}
	  Int hash1 = sa.rh.get(l, l + X);
	  Int hash2 = sa.rh.get(idx, idx + X);
	  hash1 != hash2? R=M:L=M;
	}
	ll = inv[l] - L;
      }
      
      int rr;
      {
	int L = 0, R = N;
	while(L + 1 < R){
	  int M = (L + R) / 2;
	  if(inv[l] + M >= N) {R = M; continue;}
	  int idx = sa.sa[inv[l] + M];
	  if(idx + X > N) {R = M; continue;}
	  Int hash1 = sa.rh.get(l, l + X);
	  Int hash2 = sa.rh.get(idx, idx + X);
	  hash1 != hash2? R=M:L=M;
	}
	rr = inv[l] + R;
      }
     
      
      //int ll = sa.lower_bound(sa.rh, l, l + X);
      //int rr = sa.upper_bound(sa.rh, l, l + X);
      int a = seg.get(ll, rr, l + X + 1);
      if(a == -1) return 0;
      int b = seg.get(ll, rr, a + X + 1);
      if(b == -1) return 0;
      if(b + X > r) return 0;
      return 1;
    };
    
    int L = 0, R = (r - l + 1 + 1) / 3;
    while(L+1 < R){
      int M = (L + R) / 2;
      check(M)? L = M:R = M;
    }
    cout<<L<<"\n";
  }

  return 0;
}
accuracy: 1
time_ms: 2600
memory_kb: 58008
final_rank: 6

code_snippet: #include<iomanip>
#include<limits>
#include<thread>
#include<utility>
#include<iostream>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<numeric>
#include<cassert>
#include<random>
#include<chrono>
#include<unordered_set>
#include<unordered_map>
#include<fstream>
#include<list>
#include<functional>
#include<bitset>
#include<complex>
#include<tuple>
using namespace std;
typedef unsigned long long int ull;
typedef long long int ll;
typedef pair<ll,ll> pll;
typedef pair<int,int> pi;
typedef pair<double,double> pd;
typedef pair<double,ll> pdl;
#define F first
#define S second
const ll E=1e18+7;
const ll MOD=1000000007;

static const ll High=25;
static const ll MAX_N=500001;

bool Matrix[High][MAX_N]; //下から上
pair<int,int> Rank[High][MAX_N]; //(0,1) [high][size] 自分より前
int Mid[High]; //0-origin

struct WaveletMatrix{
    const int high;
    const int size;
    
    WaveletMatrix(int high,int size,const vector<ll> &A):high(high),size(size){init(A);}
    
    void init(vector<ll> A){
        for(int i=high-1;i>=0;i--){
            pair<int,int> R={0,0};
            for(int t=0;t<size;t++){
                Matrix[i][t]=A[t]>>i&1;
                Rank[i][t]=R;
                if(Matrix[i][t]){R.S++;}
                else{R.F++;}
            }
            Rank[i][size]=R;
            vector<ll> N(size);
            int l=0;
            for(int t=0;t<size;t++){
                if((A[t]>>i&1)==0){N[l]=A[t]; l++;}
            }
            Mid[i]=l;
            for(int t=0;t<size;t++){
                if(A[t]>>i&1){N[l]=A[t]; l++;}
            }
            A=N;
        }
    }
    
    ll access(int n) const {return access(high-1,n);}
    
    ll access(int h,int n) const {
        if(h==0){return Matrix[h][n];}
        if(Matrix[h][n]){
            return (1LL<<h)|access(h-1,Mid[h]+Rank[h][n].S);
        }
        return access(h-1,Rank[h][n].F);
    }
    
    //[l,r)
    int rank(ll s,int l,int r){
        return rank(s,l,r,high-1);
    }
    
    //[l,r)
    int rank(ll s,int l,int r,int h){
        if(h==0){
            if(s&1){return Rank[h][r].S-Rank[h][l].S;}
            return Rank[h][r].F-Rank[h][l].F;
        }
        if(s>>h&1){
            return rank(s,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        }
        return rank(s,Rank[h][l].F,Rank[h][r].F,h-1);
    }
    
    //[l,r)
    ll range_min(int l,int r){return range_min(l,r,high-1);}
    
    //[l,r)
    ll range_min(int l,int r,int h){
        if(h==0){
            if(Rank[h][r].F-Rank[h][l].F==0){return 1;}
            return 0;
        }
        if(Rank[h][r].F-Rank[h][l].F==0){
            return (1LL<<h)|range_min(Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        }
        return range_min(Rank[h][l].F,Rank[h][r].F,h-1);
    }
    
    //[l,r)
    inline ll range_max(int l,int r){return range_max(l,r,high-1);}
    
    //[l,r)
    ll range_max(int l,int r,int h){
        if(h==0){
            if(Rank[h][r].S-Rank[h][l].S==0){return 0;}
            return 1;
        }
        if(Rank[h][r].S-Rank[h][l].S==0){
            return range_max(Rank[h][l].F,Rank[h][r].F,h-1);
        }
        return (1LL<<h)|range_max(Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
    }
    
    //[s,t) [l,r)
    int range_count(ll s,ll t,int l,int r){return range_count(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    int range_count(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return 0;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return r-l;}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return 0;}
        int ret=0;
        if(h==0){
            if(s<=w && w<t){ret+=Rank[h][r].F-Rank[h][l].F;}
            if(s<=w+1 && w+1<t){ret+=Rank[h][r].S-Rank[h][l].S;}
            return ret;
        }
        ret+=range_count(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        ret+=range_count(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        return ret;
    }
    
    //[s,t) [l,r)
    inline ll range_min(ll s,ll t,int l,int r){return range_min(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    ll range_min(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return -1;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return range_min(l,r,h);}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return -1;}
        ll ret=-1;
        if(h==0){
            if(s<=w && w<t && Rank[h][r].F-Rank[h][l].F>0){return 0;}
            if(s<=w+1 && w+1<t && Rank[h][r].S-Rank[h][l].S>0){return 1;}
            return -1;
        }
        ret=range_min(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        if(ret==-1){
            ret=range_min(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
            if(ret==-1){return ret;}
            ret|=1LL<<h;
        }
        return ret;
    }
    
    //[s,t) [l,r)
    inline ll range_max(ll s,ll t,int l,int r){return range_max(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    ll range_max(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return -1;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return range_max(l,r,h);}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return -1;}
        if(h==0){
            if(s<=w+1 && w+1<t && Rank[h][r].S-Rank[h][l].S>0){return 1;}
            if(s<=w && w<t && Rank[h][r].F-Rank[h][l].F>0){return 0;}
            return -1;
        }
        ll ret=-1;
        ret=range_max(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        if(ret==-1){
            ret=range_max(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        }
        else{ret|=1LL<<h;}
        return ret;
    }
};




struct node{
    int idx,num;
    node* next;
    
    inline bool operator < (const node &A) const {
        if(num!=A.num){return num<A.num;}
        return (next==NULL?-1:next->num)<(A.next==NULL?-1:A.next->num);
    }
    
    inline bool operator == (const node &A) const {
        return num==A.num && (next==NULL?-1:next->num)==(A.next==NULL?-1:A.next->num);
    }
};

ll n,q;
string s;


node A[High][MAX_N];
ll B[High][MAX_N];


inline bool same(ll len,ll h,ll idx,ll idx2){
    idx=A[High-1][idx].idx;
    idx2=A[High-1][idx2].idx;
    idx-=len;
    idx2-=len;
    return (idx<0 || idx>=n?-1:A[h][B[h][idx]].num)==(idx2<0 || idx2>=n?-1:A[h][B[h][idx2]].num);
}


int main(){
    cin.tie(0);
    ios_base::sync_with_stdio(0);
    cin>>n>>q;
    cin>>s;
    for(int t=0;t<n;t++){
        A[0][t].idx=t;
        A[0][t].num=s[t]-'a';
        A[0][t].next=NULL;
    }
    sort(&A[0][0],&A[0][n]);
    for(int t=0;t<n;t++){B[0][A[0][t].idx]=t;}
    for(int t=0;t<n;t++){A[0][t].next=(A[0][t].idx-1<0?NULL:&A[0][B[0][A[0][t].idx-1]]);}
    for(int i=1;i<High;i++){
        for(int t=0;t<n;t++){A[i][t]=A[i-1][t];}
        sort(&A[i][0],&A[i][n]);
        A[i][0].num=0;
        for(int t=0;t<n;t++){
            B[i][A[i][t].idx]=t;
            if(t>0 && A[i-1][B[i-1][A[i][t-1].idx]]==A[i][t]){A[i][t].num=A[i][t-1].num;}
            else if(t>0){A[i][t].num=A[i][t-1].num+1;}
        }
        for(int t=0;t<n;t++){
            A[i][t].next=(A[i][t].idx-(1LL<<i)<0?NULL:&A[i][B[i][A[i][t].idx-(1LL<<i)]]);
        }
    }
    vector<ll> a(n);
    for(int i=0;i<n;i++){
        a[i]=A[High-1][i].idx;
    }
    WaveletMatrix M(High,(int)n,a);
    while(q--){
        ll l,r;
        cin>>l>>r;
        l--; r--;
        ll h=High;
        ll len=0;
        ll L=-1,R=n;
        while((--h)>=0){
            ll test=len+(1LL<<h);
            ll r1=r-test;
            if(r1<0){continue;}
            ll left=L;
            ll k=High;
            while((--k)>=0){
                if(left+(1LL<<k)>=B[High-1][r]){continue;}
                if(!same(len,h,left+(1LL<<k),B[High-1][r])){left+=1LL<<k;}
            }
            k=High;
            ll right=R;
            while((--k)>=0){
                if(right-(1LL<<k)<=B[High-1][r]){continue;}
                if(!same(len,h,right-(1LL<<k),B[High-1][r])){right-=1LL<<k;}
            }
            ll r2=M.range_max(l,r1,(int)left+1,(int)right);
            r2-=test;
            if(r2<=0){continue;}
            r2=M.range_max(l,r2,(int)left+1,(int)right);
            if(r2-test>=l){
                len+=1LL<<h;
                L=left; R=right;
            }
        }
        cout<<len<<'\n';
    }
    
    
    
    
    
    return 0;
}
accuracy: 1
time_ms: 3950
memory_kb: 169228
final_rank: 19

code_snippet: #include<iomanip>
#include<limits>
#include<thread>
#include<utility>
#include<iostream>
#include<string>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<numeric>
#include<cassert>
#include<random>
#include<chrono>
#include<unordered_set>
#include<unordered_map>
#include<fstream>
#include<list>
#include<functional>
#include<bitset>
#include<complex>
#include<tuple>
using namespace std;
typedef unsigned long long int ull;
typedef long long int ll;
typedef pair<ll,ll> pll;
typedef pair<int,int> pi;
typedef pair<double,double> pd;
typedef pair<double,ll> pdl;
#define F first
#define S second
const ll E=1e18+7;
const ll MOD=1000000007;

static const ll High=20;
static const ll MAX_N=200001;

bool Matrix[High][MAX_N]; //下から上
pair<int,int> Rank[High][MAX_N]; //(0,1) [high][size] 自分より前
int Mid[High]; //0-origin

struct WaveletMatrix{
    const int high;
    const int size;
    
    WaveletMatrix(int high,int size,const vector<ll> &A):high(high),size(size){init(A);}
    
    void init(vector<ll> A){
        for(int i=high-1;i>=0;i--){
            pair<int,int> R={0,0};
            for(int t=0;t<size;t++){
                Matrix[i][t]=A[t]>>i&1;
                Rank[i][t]=R;
                if(Matrix[i][t]){R.S++;}
                else{R.F++;}
            }
            Rank[i][size]=R;
            vector<ll> N(size);
            int l=0;
            for(int t=0;t<size;t++){
                if((A[t]>>i&1)==0){N[l]=A[t]; l++;}
            }
            Mid[i]=l;
            for(int t=0;t<size;t++){
                if(A[t]>>i&1){N[l]=A[t]; l++;}
            }
            A=N;
        }
    }
    
    ll access(int n) const {return access(high-1,n);}
    
    ll access(int h,int n) const {
        if(h==0){return Matrix[h][n];}
        if(Matrix[h][n]){
            return (1LL<<h)|access(h-1,Mid[h]+Rank[h][n].S);
        }
        return access(h-1,Rank[h][n].F);
    }
    
    //[l,r)
    int rank(ll s,int l,int r){
        return rank(s,l,r,high-1);
    }
    
    //[l,r)
    int rank(ll s,int l,int r,int h){
        if(h==0){
            if(s&1){return Rank[h][r].S-Rank[h][l].S;}
            return Rank[h][r].F-Rank[h][l].F;
        }
        if(s>>h&1){
            return rank(s,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        }
        return rank(s,Rank[h][l].F,Rank[h][r].F,h-1);
    }
    
    //[l,r)
    ll range_min(int l,int r){return range_min(l,r,high-1);}
    
    //[l,r)
    ll range_min(int l,int r,int h){
        if(h==0){
            if(Rank[h][r].F-Rank[h][l].F==0){return 1;}
            return 0;
        }
        if(Rank[h][r].F-Rank[h][l].F==0){
            return (1LL<<h)|range_min(Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        }
        return range_min(Rank[h][l].F,Rank[h][r].F,h-1);
    }
    
    //[l,r)
    inline ll range_max(int l,int r){return range_max(l,r,high-1);}
    
    //[l,r)
    ll range_max(int l,int r,int h){
        if(h==0){
            if(Rank[h][r].S-Rank[h][l].S==0){return 0;}
            return 1;
        }
        if(Rank[h][r].S-Rank[h][l].S==0){
            return range_max(Rank[h][l].F,Rank[h][r].F,h-1);
        }
        return (1LL<<h)|range_max(Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
    }
    
    //[s,t) [l,r)
    int range_count(ll s,ll t,int l,int r){return range_count(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    int range_count(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return 0;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return r-l;}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return 0;}
        int ret=0;
        if(h==0){
            if(s<=w && w<t){ret+=Rank[h][r].F-Rank[h][l].F;}
            if(s<=w+1 && w+1<t){ret+=Rank[h][r].S-Rank[h][l].S;}
            return ret;
        }
        ret+=range_count(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        ret+=range_count(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        return ret;
    }
    
    //[s,t) [l,r)
    inline ll range_min(ll s,ll t,int l,int r){return range_min(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    ll range_min(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return -1;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return range_min(l,r,h);}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return -1;}
        ll ret=-1;
        if(h==0){
            if(s<=w && w<t && Rank[h][r].F-Rank[h][l].F>0){return 0;}
            if(s<=w+1 && w+1<t && Rank[h][r].S-Rank[h][l].S>0){return 1;}
            return -1;
        }
        ret=range_min(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        if(ret==-1){
            ret=range_min(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
            if(ret==-1){return ret;}
            ret|=1LL<<h;
        }
        return ret;
    }
    
    //[s,t) [l,r)
    inline ll range_max(ll s,ll t,int l,int r){return range_max(0,s,t,l,r,high-1);}
    
    //[s,t) [l,r)
    ll range_max(ll w,ll s,ll t,int l,int r,int h){
        if(r<=l || t<=s){return -1;}
        if(s<=w && (w+(1LL<<(h+1)))<=t){return range_max(l,r,h);}
        if(s>=(w+(1LL<<(h+1))) || w>=t){return -1;}
        if(h==0){
            if(s<=w+1 && w+1<t && Rank[h][r].S-Rank[h][l].S>0){return 1;}
            if(s<=w && w<t && Rank[h][r].F-Rank[h][l].F>0){return 0;}
            return -1;
        }
        ll ret=-1;
        ret=range_max(w|(1LL<<h),s,t,Mid[h]+Rank[h][l].S,Mid[h]+Rank[h][r].S,h-1);
        if(ret==-1){
            ret=range_max(w,s,t,Rank[h][l].F,Rank[h][r].F,h-1);
        }
        else{ret|=1LL<<h;}
        return ret;
    }
};




struct node{
    int idx,num;
    node* next;
    
    inline bool operator < (const node &A) const {
        if(num!=A.num){return num<A.num;}
        return (next==NULL?-1:next->num)<(A.next==NULL?-1:A.next->num);
    }
    
    inline bool operator == (const node &A) const {
        return num==A.num && (next==NULL?-1:next->num)==(A.next==NULL?-1:A.next->num);
    }
};

ll n,q;
string s;


node A[High][MAX_N];
ll B[High][MAX_N];


inline bool same(ll len,ll h,ll idx,ll idx2){
    idx=A[High-1][idx].idx;
    idx2=A[High-1][idx2].idx;
    idx-=len;
    idx2-=len;
    return (idx<0 || idx>=n?-1:A[h][B[h][idx]].num)==(idx2<0 || idx2>=n?-1:A[h][B[h][idx2]].num);
}


int main(){
    cin.tie(0);
    ios_base::sync_with_stdio(0);
    cin>>n>>q;
    cin>>s;
    for(int t=0;t<n;t++){
        A[0][t].idx=t;
        A[0][t].num=s[t]-'a';
        A[0][t].next=NULL;
    }
    sort(&A[0][0],&A[0][n]);
    for(int t=0;t<n;t++){B[0][A[0][t].idx]=t;}
    for(int t=0;t<n;t++){A[0][t].next=(A[0][t].idx-1<0?NULL:&A[0][B[0][A[0][t].idx-1]]);}
    for(int i=1;i<High;i++){
        for(int t=0;t<n;t++){A[i][t]=A[i-1][t];}
        sort(&A[i][0],&A[i][n]);
        A[i][0].num=0;
        for(int t=0;t<n;t++){
            B[i][A[i][t].idx]=t;
            if(t>0 && A[i-1][B[i-1][A[i][t-1].idx]]==A[i][t]){A[i][t].num=A[i][t-1].num;}
            else if(t>0){A[i][t].num=A[i][t-1].num+1;}
        }
        for(int t=0;t<n;t++){
            A[i][t].next=(A[i][t].idx-(1LL<<i)<0?NULL:&A[i][B[i][A[i][t].idx-(1LL<<i)]]);
        }
    }
    vector<ll> a(n);
    for(int i=0;i<n;i++){
        a[i]=A[High-1][i].idx;
    }
    WaveletMatrix M(High,(int)n,a);
    while(q--){
        ll l,r;
        cin>>l>>r;
        l--; r--;
        ll h=High;
        ll len=0;
        ll L=-1,R=n;
        while((--h)>=0){
            ll test=len+(1LL<<h);
            ll r1=r-test;
            if(r1<0){continue;}
            ll left=L;
            ll k=High;
            while((--k)>=0){
                if(left+(1LL<<k)>=B[High-1][r]){continue;}
                if(!same(len,h,left+(1LL<<k),B[High-1][r])){left+=1LL<<k;}
            }
            k=High;
            ll right=R;
            while((--k)>=0){
                if(right-(1LL<<k)<=B[High-1][r]){continue;}
                if(!same(len,h,right-(1LL<<k),B[High-1][r])){right-=1LL<<k;}
            }
            ll r2=M.range_max(l,r1,(int)left+1,(int)right);
            r2-=test;
            if(r2<=0){continue;}
            r2=M.range_max(l,r2,(int)left+1,(int)right);
            if(r2-test>=l){
                len+=1LL<<h;
                L=left; R=right;
            }
        }
        cout<<len<<'\n';
    }
    
    
    
    
    
    return 0;
}
accuracy: 1
time_ms: 3150
memory_kb: 136596
final_rank: 14