using System; using System.Collections.Generic; using System.IO; using System.Linq; using static System.Math; using System.Text; using System.Threading; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Library; namespace Program { public static class ProblemA { static bool SAIKI = false; static public int numberOfRandomCases = 0; static public void MakeTestCase(List _input, List _output, ref Func _outputChecker) { } static public void Solve() { var N = NN; var Q = NN; var S = NS; var LR = Repeat(0, Q).Select(_ => new { L = (int)NN - 1, R = (int)NN }).ToArray(); var lcparray = LIB_String.SuffixArray(S); var ruiseki = new long[lcparray.Length + 1]; for (var i = 1; i <= lcparray.Length; ++i) { ruiseki[i] = ruiseki[i - 1] + N - lcparray[i - 1]; } foreach (var item in LR) { var str = S.Substring(item.L, item.R - item.L); var pivot1 = 0L; var pivot2 = 0L; { var left = -1L; var right = N; while (right - left > 1) { var mid = (right + left) / 2; var lastlen = N - lcparray[mid]; var teststr = S.Substring((int)lcparray[mid], (int)Min(lastlen, str.Length)); if (teststr.CompareTo(str) < 0) left = mid; else right = mid; } pivot1 = left; } { var left = -1L; var right = N; while (right - left > 1) { var mid = (right + left) / 2; var lastlen = N - lcparray[mid]; var teststr = S.Substring((int)lcparray[mid], (int)Min(lastlen, str.Length)); if (teststr.CompareTo(str) <= 0) left = mid; else right = mid; } pivot2 = left; } var ans = (pivot2 - pivot1) * (str.Length - 1) + ruiseki[pivot1 + 1]; Console.WriteLine(ans); } } class Printer : StreamWriter { public override IFormatProvider FormatProvider { get { return CultureInfo.InvariantCulture; } } public Printer(Stream stream) : base(stream, new UTF8Encoding(false, true)) { base.AutoFlush = false; } public Printer(Stream stream, Encoding encoding) : base(stream, encoding) { base.AutoFlush = false; } } static LIB_FastIO fastio = new LIB_FastIODebug(); static string[] args; static public void Main(string[] args_t) { args = args_t; if (args_t.Length == 0) { fastio = new LIB_FastIO(); Console.SetOut(new Printer(Console.OpenStandardOutput())); } if (SAIKI) { var t = new Thread(Solve, 134217728); t.Start(); t.Join(); } else Solve(); Console.Out.Flush(); } static long NN => fastio.Long(); static double ND => fastio.Double(); static string NS => fastio.Scan(); static long[] NNList(long N) => Repeat(0, N).Select(_ => NN).ToArray(); static double[] NDList(long N) => Repeat(0, N).Select(_ => ND).ToArray(); static string[] NSList(long N) => Repeat(0, N).Select(_ => NS).ToArray(); static long Count(this IEnumerable x, Func pred) => Enumerable.Count(x, pred); static IEnumerable Repeat(T v, long n) => Enumerable.Repeat(v, (int)n); static IEnumerable Range(long s, long c) => Enumerable.Range((int)s, (int)c); static IOrderedEnumerable OrderByRand(this IEnumerable x) => Enumerable.OrderBy(x, _ => xorshift); static IOrderedEnumerable OrderBy(this IEnumerable x) => Enumerable.OrderBy(x.OrderByRand(), e => e); static IOrderedEnumerable OrderBy(this IEnumerable x, Func selector) => Enumerable.OrderBy(x.OrderByRand(), selector); static IOrderedEnumerable OrderByDescending(this IEnumerable x) => Enumerable.OrderByDescending(x.OrderByRand(), e => e); static IOrderedEnumerable OrderByDescending(this IEnumerable x, Func selector) => Enumerable.OrderByDescending(x.OrderByRand(), selector); static IOrderedEnumerable OrderBy(this IEnumerable x) => x.OrderByRand().OrderBy(e => e, StringComparer.OrdinalIgnoreCase); static IOrderedEnumerable OrderBy(this IEnumerable x, Func selector) => x.OrderByRand().OrderBy(selector, StringComparer.OrdinalIgnoreCase); static IOrderedEnumerable OrderByDescending(this IEnumerable x) => x.OrderByRand().OrderByDescending(e => e, StringComparer.OrdinalIgnoreCase); static IOrderedEnumerable OrderByDescending(this IEnumerable x, Func selector) => x.OrderByRand().OrderByDescending(selector, StringComparer.OrdinalIgnoreCase); static string Join(this IEnumerable x, string separator = "") => string.Join(separator, x); static uint xorshift { get { _xsi.MoveNext(); return _xsi.Current; } } static IEnumerator _xsi = _xsc(); static IEnumerator _xsc() { uint x = 123456789, y = 362436069, z = 521288629, w = (uint)(DateTime.Now.Ticks & 0xffffffff); while (true) { var t = x ^ (x << 11); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); yield return w; } } static bool Chmax(this ref T lhs, T rhs) where T : struct, IComparable { if (lhs.CompareTo(rhs) < 0) { lhs = rhs; return true; } return false; } static bool Chmin(this ref T lhs, T rhs) where T : struct, IComparable { if (lhs.CompareTo(rhs) > 0) { lhs = rhs; return true; } return false; } static void Fill(this T[] array, T value) => array.AsSpan().Fill(value); static void Fill(this T[,] array, T value) => MemoryMarshal.CreateSpan(ref array[0, 0], array.Length).Fill(value); static void Fill(this T[,,] array, T value) => MemoryMarshal.CreateSpan(ref array[0, 0, 0], array.Length).Fill(value); static void Fill(this T[,,,] array, T value) => MemoryMarshal.CreateSpan(ref array[0, 0, 0, 0], array.Length).Fill(value); } } namespace Library { class LIB_BitUtil { public static readonly ulong[] BitMask; static LIB_BitUtil() { BitMask = new ulong[64]; BitMask[0] = 1; for (var i = 1; i < 64; i++) BitMask[i] = BitMask[i - 1] << 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public IEnumerable ScanOne(long value) { for (; value > 0; value &= value - 1) yield return value & -value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public int MSB(long value) { return 64 - (int)System.Runtime.Intrinsics.X86.Lzcnt.X64.LeadingZeroCount((ulong)value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public int LSB(long value) { return 64 - (int)System.Runtime.Intrinsics.X86.Lzcnt.X64.LeadingZeroCount((ulong)(value & -value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public long PopCount(ulong value) { return (long)System.Runtime.Intrinsics.X86.Popcnt.X64.PopCount(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public long PopCount(long value) => PopCount((ulong)value); [MethodImpl(MethodImplOptions.AggressiveInlining)] static public bool IsSet(ulong value, int idx) => (value & BitMask[idx]) != 0; [MethodImpl(MethodImplOptions.AggressiveInlining)] static public bool IsSet(long value, int idx) => IsSet((ulong)value, idx); [MethodImpl(MethodImplOptions.AggressiveInlining)] static public T[] BitZentansaku(long bitlen, T e, Func f) { var retlen = 1 << (int)bitlen; var ret = new T[retlen]; var idx = 0; ret[0] = e; for (var i = 1; i < retlen; ++i) { if (i == (1 << (idx + 1))) ++idx; ret[i] = f(idx, ret[i & (~(1 << idx))]); } return ret; } } class LIB_FastIO { [MethodImpl(MethodImplOptions.AggressiveInlining)] public LIB_FastIO() { str = Console.OpenStandardInput(); } readonly Stream str; readonly byte[] buf = new byte[2048]; int len, ptr; [MethodImpl(MethodImplOptions.AggressiveInlining)] byte read() { if (ptr >= len) { ptr = 0; if ((len = str.Read(buf, 0, 2048)) <= 0) { return 0; } } return buf[ptr++]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] char Char() { byte b = 0; do b = read(); while (b < 33 || 126 < b); return (char)b; } [MethodImpl(MethodImplOptions.AggressiveInlining)] virtual public string Scan() { var sb = new StringBuilder(); for (var b = Char(); b >= 33 && b <= 126; b = (char)read()) sb.Append(b); return sb.ToString(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] virtual public long Long() { long ret = 0; byte b = 0; var ng = false; do b = read(); while (b != '-' && (b < '0' || '9' < b)); if (b == '-') { ng = true; b = read(); } for (; true; b = read()) { if (b < '0' || '9' < b) return ng ? -ret : ret; else ret = (ret << 3) + (ret << 1) + b - '0'; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] virtual public double Double() { return double.Parse(Scan(), CultureInfo.InvariantCulture); } } class LIB_FastIODebug : LIB_FastIO { Queue param = new Queue(); [MethodImpl(MethodImplOptions.AggressiveInlining)] string NextString() { if (param.Count == 0) foreach (var item in Console.ReadLine().Split(' ')) param.Enqueue(item); return param.Dequeue(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LIB_FastIODebug() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string Scan() => NextString(); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override long Long() => long.Parse(NextString()); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override double Double() => double.Parse(NextString()); } class LIB_Bitset : IEquatable { long n; public ulong[] ary; static readonly ulong[] ceil; static LIB_Bitset() { ceil = new ulong[64]; ceil[0] = 0xffffffffffffffff; ceil[1] = 1; for (var i = 2; i < 64; i++) { ceil[i] = (ceil[i - 1] << 1) | 1; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LIB_Bitset(long size) { if (size <= 0) throw new Exception(); n = size; ary = new ulong[((n - 1) >> 6) + 1]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public LIB_Bitset(LIB_Bitset src) { n = src.n; ary = src.ary.ToArray(); } public long Count => n; public long PopCount => ary.Sum(e => LIB_BitUtil.PopCount(e)); public bool this[int idx] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return LIB_BitUtil.IsSet(ary[idx >> 6], idx & 63); } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { if (value) ary[idx >> 6] |= LIB_BitUtil.BitMask[idx & 63]; else ary[idx >> 6] &= ~LIB_BitUtil.BitMask[idx & 63]; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator &(LIB_Bitset x, LIB_Bitset y) { if (x.n < y.n) { var t = x; x = y; y = t; } var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = x.ary[i]; for (var i = 0; i < y.ary.Length; i++) ret.ary[i] &= y.ary[i]; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator |(LIB_Bitset x, LIB_Bitset y) { if (x.n < y.n) { var t = x; x = y; y = t; } var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = x.ary[i]; for (var i = 0; i < y.ary.Length; i++) ret.ary[i] |= y.ary[i]; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void OrEqual(LIB_Bitset y) { if (n < y.n) { n = y.n; var tmp = new ulong[y.n]; for (var i = 0; i < ary.Length; i++) tmp[i] = ary[i]; ary = tmp; } for (var i = 0; i < y.ary.Length; i++) ary[i] |= y.ary[i]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator ^(LIB_Bitset x, LIB_Bitset y) { if (x.n < y.n) { var t = x; x = y; y = t; } var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = x.ary[i]; for (var i = 0; i < y.ary.Length; i++) ret.ary[i] ^= y.ary[i]; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator &(LIB_Bitset x, long y) { var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = x.ary[i]; ret.ary[0] &= (ulong)y; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator |(LIB_Bitset x, long y) { var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = x.ary[i]; ret.ary[0] |= (ulong)y; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator ^(LIB_Bitset x, long y) { var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = x.ary[i]; ret.ary[0] ^= (ulong)y; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Flip() { for (var i = 0; i < ary.Length; i++) ary[i] = ~ary[i]; ary[ary.Length - 1] &= ceil[n & 63]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator ~(LIB_Bitset x) { var ret = new LIB_Bitset(x.n); for (var i = 0; i < ret.ary.Length; i++) ret.ary[i] = ~x.ary[i]; ret.ary[ret.ary.Length - 1] &= ceil[ret.n & 63]; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ShiftLeft(int num) { var moveCnt = num >> 6; var moveBit = num & 63; var i = ary.Length - 1; for (; i >= moveCnt; i--) { ary[i] = ary[i - moveCnt] << moveBit; if (moveBit > 0 && i > moveCnt) ary[i] |= ary[i - moveCnt - 1] >> (64 - moveBit); } for (; i >= 0; --i) ary[i] = 0; ary[ary.Length - 1] &= ceil[n & 63]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator <<(LIB_Bitset x, int num) { var ret = new LIB_Bitset(x.n); var moveCnt = num >> 6; var moveBit = num & 63; var i = ret.ary.Length - 1; for (; i >= moveCnt; i--) { ret.ary[i] = x.ary[i - moveCnt] << moveBit; if (moveBit > 0 && i > moveCnt) ret.ary[i] |= x.ary[i - moveCnt - 1] >> (64 - moveBit); } for (; i >= 0; --i) ret.ary[i] = 0; ret.ary[ret.ary.Length - 1] &= ceil[ret.n & 63]; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ShiftRight(int num) { var moveCnt = num >> 6; var moveBit = num & 63; var aryMax = ary.Length - moveCnt; var i = 0; for (; i < aryMax; i++) { ary[i] = ary[i + moveCnt] >> moveBit; if (moveBit > 0 && i < aryMax - 1) ary[i] |= ary[i + moveCnt + 1] << (64 - moveBit); } for (; i < ary.Length; ++i) ary[i] = 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public LIB_Bitset operator >>(LIB_Bitset x, int num) { var ret = new LIB_Bitset(x.n); var moveCnt = num >> 6; var moveBit = num & 63; var aryMax = ret.ary.Length - moveCnt; var i = 0; for (; i < aryMax; i++) { ret.ary[i] = x.ary[i + moveCnt] >> moveBit; if (moveBit > 0 && i < aryMax - 1) ret.ary[i] |= x.ary[i + moveCnt + 1] << (64 - moveBit); } for (; i < ret.ary.Length; ++i) ret.ary[i] = 0; return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public bool operator ==(LIB_Bitset x, LIB_Bitset y) { if (x.n < y.n) { var t = x; x = y; y = t; } var i = 0; for (; i < y.ary.Length; i++) if (x.ary[i] != y.ary[i]) return false; for (; i < x.ary.Length; i++) if (x.ary[i] != 0) return false; return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] static public bool operator !=(LIB_Bitset x, LIB_Bitset y) => !(x == y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(LIB_Bitset x) => x == this; [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object x) => x == null ? false : Equals((LIB_Bitset)x); [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { var t = ary.Aggregate((a, x) => a ^ x); return (int)(((t >> 32) ^ t) & 0x000000007fffffff); } } class LIB_String { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int[] ZAlgorithm(long[] s) { var ret = new int[s.Length]; ret[0] = s.Length; int i = 1, j = 0; while (i < s.Length) { while (i + j < s.Length && s[j] == s[i + j]) ++j; ret[i] = j; if (j == 0) { ++i; continue; } var k = 1; while (i + k < s.Length && k + ret[k] < j) { ret[i + k] = ret[k]; ++k; } i += k; j -= k; } return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] static int[] SAIS(int[] s, int upper) { var n = s.Length; if (n == 0) return new int[0]; if (n == 1) return new[] { 0 }; if (n == 2) { if (s[0] < s[1]) return new[] { 0, 1 }; return new[] { 1, 0 }; } ref int sref = ref s[0]; var ls = new bool[n]; ref bool lsref = ref ls[0]; var befores = Unsafe.Add(ref sref, n - 1); var beforels = Unsafe.Add(ref lsref, n - 1); for (var i = n - 2; i >= 0; --i) { var siref = Unsafe.Add(ref sref, i); beforels = Unsafe.Add(ref lsref, i) = siref == befores ? beforels : siref < befores; befores = siref; } var suml = new int[upper + 1]; var sums = new int[upper + 1]; ref int sumlref = ref suml[0]; ref int sumsref = ref sums[0]; for (var i = 0; i < n; ++i) { if (!Unsafe.Add(ref lsref, i)) ++Unsafe.Add(ref sumsref, Unsafe.Add(ref sref, i)); else ++Unsafe.Add(ref sumlref, Unsafe.Add(ref sref, i) + 1); } for (var i = 0; i <= upper; ++i) { ref int sumsiref = ref Unsafe.Add(ref sumsref, i); sumsiref += Unsafe.Add(ref sumlref, i); if (i < upper) Unsafe.Add(ref sumlref, i + 1) += sumsiref; } var sa = new int[n]; ref int saref = ref sa[0]; void induce(ref int plmsref, int len, ref int sref, ref bool lsref, ref int sumsref, ref int sumlref, ref int saref) { Unsafe.InitBlock(ref Unsafe.As(ref saref), 255, (uint)(4 * n)); var buf = new int[upper + 1]; ref int bufref = ref buf[0]; Unsafe.CopyBlock(ref Unsafe.As(ref bufref), ref Unsafe.As(ref sumsref), (uint)(4 * (upper + 1))); for (var i = 0; i < len; ++i) { var d = Unsafe.Add(ref plmsref, i); if (d == n) continue; Unsafe.Add(ref saref, Unsafe.Add(ref bufref, Unsafe.Add(ref sref, d))++) = d; } Unsafe.CopyBlock(ref Unsafe.As(ref bufref), ref Unsafe.As(ref sumlref), (uint)(4 * (upper + 1))); Unsafe.Add(ref saref, Unsafe.Add(ref bufref, Unsafe.Add(ref sref, n - 1))++) = n - 1; for (var i = 0; i < n; ++i) { var v = Unsafe.Add(ref saref, i); if (v >= 1 && !Unsafe.Add(ref lsref, v - 1)) Unsafe.Add(ref saref, Unsafe.Add(ref bufref, Unsafe.Add(ref sref, v - 1))++) = v - 1; } Unsafe.CopyBlock(ref Unsafe.As(ref bufref), ref Unsafe.As(ref sumlref), (uint)(4 * (upper + 1))); for (var i = n - 1; i >= 0; --i) { var v = Unsafe.Add(ref saref, i); if (v >= 1 && Unsafe.Add(ref lsref, v - 1)) Unsafe.Add(ref saref, --Unsafe.Add(ref bufref, Unsafe.Add(ref sref, v - 1) + 1)) = v - 1; } } var lmsMap = new int[n + 1]; ref int lmsMapref = ref lmsMap[0]; Unsafe.Add(ref lmsMapref, 0) = Unsafe.Add(ref lmsMapref, n) = -1; var m = 0; var beforelsi = Unsafe.Add(ref lsref, 0); for (var i = 1; i < n; ++i) { var lsi = Unsafe.Add(ref lsref, i); if (!beforelsi && lsi) Unsafe.Add(ref lmsMapref, i) = m++; else Unsafe.Add(ref lmsMapref, i) = -1; beforelsi = lsi; } if (m == 0) induce(ref lmsMapref, 0, ref sref, ref lsref, ref sumsref, ref sumlref, ref saref); else { var lms = new int[m]; ref int lmsref = ref lms[0]; var lmsIdx = -1; beforelsi = Unsafe.Add(ref lsref, 0); for (var i = 1; i < n; ++i) { var lsi = Unsafe.Add(ref lsref, i); if (!beforelsi && lsi) Unsafe.Add(ref lmsref, ++lmsIdx) = i; beforelsi = lsi; } induce(ref lmsref, lmsIdx + 1, ref sref, ref lsref, ref sumsref, ref sumlref, ref saref); var sortedLms = new int[m]; ref int sortedLmsref = ref sortedLms[0]; var sortedLmsIdx = -1; for (var i = 0; i < n; ++i) { var v = Unsafe.Add(ref saref, i); if (Unsafe.Add(ref lmsMapref, v) != -1) Unsafe.Add(ref sortedLmsref, ++sortedLmsIdx) = v; } var recs = new int[m]; ref int recsref = ref recs[0]; var recUpper = 0; var beforeSortedLms = sortedLmsref; for (var i = 1; i < m; ++i) { var l = beforeSortedLms; var sortedLmsi = Unsafe.Add(ref sortedLmsref, i); var r = sortedLmsi; var lmsMapl = Unsafe.Add(ref lmsMapref, l); var lmsMapr = Unsafe.Add(ref lmsMapref, r); var endl = lmsMapl + 1 < m ? Unsafe.Add(ref lmsref, lmsMapl + 1) : n; var endr = lmsMapr + 1 < m ? Unsafe.Add(ref lmsref, lmsMapr + 1) : n; var same = true; if (endl - l != endr - r) same = false; else { while (l < endl) { if (Unsafe.Add(ref sref, l) != Unsafe.Add(ref sref, r)) break; ++l; ++r; } if (l == n || Unsafe.Add(ref sref, l) != Unsafe.Add(ref sref, r)) same = false; } if (!same) ++recUpper; Unsafe.Add(ref recsref, Unsafe.Add(ref lmsMapref, sortedLmsi)) = recUpper; beforeSortedLms = sortedLmsi; } var recsa = SAIS(recs, recUpper); ref int recsaref = ref recsa[0]; for (var i = 0; i < m; ++i) Unsafe.Add(ref sortedLmsref, i) = Unsafe.Add(ref lmsref, Unsafe.Add(ref recsaref, i)); induce(ref sortedLmsref, sortedLmsIdx + 1, ref sref, ref lsref, ref sumsref, ref sumlref, ref saref); } return sa; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int[] SuffixArray(long[] ary) { var len = ary.Length; if (len == 0) return new int[0]; var idx = new int[len]; var s2 = new int[len]; ref int idxref = ref idx[0]; ref long aryref = ref ary[0]; for (var i = 0; i < len; ++i) Unsafe.Add(ref idxref, i) = i; Array.Sort(idx, (x, y) => ary[x].CompareTo(ary[y])); var now = 0; s2[idxref] = now; for (var i = 1; i < len; ++i) { ref int thisref = ref Unsafe.Add(ref idxref, i); if (Unsafe.Add(ref aryref, thisref) != Unsafe.Add(ref aryref, idxref)) ++now; idxref = ref thisref; } return SAIS(s2, now); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int[] SuffixArray(string s) { var s2 = new int[s.Length]; for (var i = 0; i < s.Length; ++i) s2[i] = (int)s[i]; return SAIS(s2, 255); } [MethodImpl(MethodImplOptions.AggressiveInlining)] static long[] LCPArray(int[] ary, int[] sa) { var len = ary.Length; if (len == 0) return new long[0]; ref int aryref = ref ary[0]; Span rnk = stackalloc int[len]; ref int rnkref = ref rnk[0]; ref int saref = ref sa[0]; for (var i = 0; i < len; ++i) Unsafe.Add(ref rnkref, Unsafe.Add(ref saref, i)) = i; var lcp = new long[len - 1]; ref long lcpref = ref lcp[0]; var h = 0; for (var i = 0; i < len; ++i) { if (h > 0) --h; var rnki = Unsafe.Add(ref rnkref, i); if (rnki == 0) continue; var j = Unsafe.Add(ref saref, rnki - 1); for (int ih = i + h, jh = j + h; jh < len && ih < len; ++ih, ++jh, ++h) { if (Unsafe.Add(ref aryref, jh) != Unsafe.Add(ref aryref, ih)) break; } Unsafe.Add(ref lcpref, rnki - 1) = h; } return lcp; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long[] LCPArray(long[] ary, int[] suffixArray) => LCPArray(ary.Select(e => (int)e).ToArray(), suffixArray); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long[] LCPArray(long[] ary) => LCPArray(ary.Select(e => (int)e).ToArray(), SuffixArray(ary)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long[] LCPArray(string s, int[] suffixArray) { var s2 = new int[s.Length]; for (var i = 0; i < s.Length; ++i) s2[i] = (int)s[i]; return LCPArray(s2, suffixArray); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long[] LCPArray(string s) { var s2 = new int[s.Length]; for (var i = 0; i < s.Length; ++i) s2[i] = (int)s[i]; var sa = SAIS(s2, 255); return LCPArray(s2, sa); } /// /// 入力の各要素は0以上。-1の要素は任意と一致。 /// マッチした位置の1文字目の位置を0-indexedで返す /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static List ShiftAnd(long[] s, long[] t) { var finish = new LIB_Bitset(t.Length); finish[t.Length - 1] = true; var mask = s.Where(e => e >= 0).Distinct().ToDictionary(e => e, _ => new LIB_Bitset(t.Length)); var fil = new LIB_Bitset(t.Length); mask[-1] = ~fil; fil |= 1; foreach (var item in t) { if (item >= 0 && mask.ContainsKey(item)) mask[item] |= fil; if (item == -1) foreach (var item2 in mask.Keys.ToArray()) mask[item2] |= fil; fil <<= 1; } var state = new LIB_Bitset(s.Length); var ret = new List(); for (var i = 0; i < s.Length; ++i) { state = ((state << 1) | 1) & mask[s[i]]; if ((state & finish) == finish) ret.Add(i - t.Length + 1); } return ret; } /// /// 編集距離。レーベンシュタイン距離。 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long EditLength(string s, string t) => EditLength(s.Select(e => (long)e).ToArray(), t.Select(e => (long)e).ToArray()); /// /// 編集距離。レーベンシュタイン距離。 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long EditLength(long[] s, long[] t) { var dp = new long[s.Length + 1, t.Length + 1]; for (var i = 1; i <= s.Length; ++i) dp[i, 0] = i; for (var j = 1; j <= t.Length; ++j) dp[0, j] = j; for (var i = 1; i <= s.Length; ++i) { for (var j = 1; j <= t.Length; ++j) { dp[i, j] = Min(dp[i - 1, j], Min(dp[i, j - 1], dp[i - 1, j - 1])) + 1; if (s[i - 1] == t[j - 1]) dp[i, j] = Min(dp[i, j], dp[i - 1, j - 1]); } } return dp[s.Length, t.Length]; } } }