Maximum Palindromes

Sort by

recency

|

95 Discussions

|

  • + 0 comments

    My answer with Typescript, Has runtime error with few very very large test 100.000+ that idk where cause 50kb limit test

    function answerQuery(l: number, r: number): number {
        let memo_factorial = new Map<bigint, bigint>([[0n, 1n], [1n, 1n]])
        function factorial(n: bigint): bigint {
            if (!memo_factorial.has(n)) {
                let i_ = Array.from(memo_factorial.keys()).pop() || 1n;
                let r_ = memo_factorial.get(i_)!
                for (let i = i_ + 1n; i <= n; i++) {
                    r_ *= i
                    memo_factorial.set(i, r_)
                }
            }
            return memo_factorial.get(n)!;
        }
    
        // 0. get the substring from [l] and [r], get [frequency]
        const s = str.substring(l - 1, r)
        const frequency = new Map<string, number>()
        for (let c of s) frequency.set(c, (frequency.get(c) || 0) + 1)
    
        /**
         *              factorial(p_alls)
         *          m * ------------------------------- (modulo 10^9 + 7)
         *              factorial(p*...)
         * 
         * with:
         *  + [m]: numbers of character that odd, mean it can be middle
         *  + [p]: colection of character that can be appears in 2 side of palidrome
         */
    
        /**
         * Update 1: Number exceeded Number.MAX_SAFE_INTEGER -> using BigInt
         * Update 2: Maximum call stack exceeded -> changes [factorial] to use for...loop insteads of recursive
         * Update 3: Some test timeout -> try memo
         * Update 4: Some test with 100.000 has runtime error, idk where cause 50kb limit custom test
         */
        
        // 1. calculate prepare for formular
        let p_alls = factorial(BigInt(Array.from(frequency.values()).reduce((_, v) => _ + Math.floor(v / 2), 0)))
        let p_each = Array.from(frequency.values()).reduce((_, v) => _ * factorial(BigInt(Math.floor(v / 2))), 1n)
        let m = BigInt(Math.max(Array.from(frequency.values()).reduce((_, v) => _ + v % 2, 0), 1))
        
        // 2. apply formular
        return Number((m * p_alls / p_each) % BigInt(10 ** 9 + 7))
    }
    
  • + 0 comments

    The trick here is to use Fermat's little teorem for computing inverse factorials and precompute binomial coeffitiens modulo 10e9 + 7, then use them for computing the multinomial coeffitients for palindromes

  • + 1 comment

    My solution

    def _FACT_TABLE():
        facts=[0, 1, 2]
        def _fact(n):
            if n<len(facts)-1:
                return facts[n]
            for i in range(len(facts), n+1):
                facts.append(facts[i-1]*i)
            return facts[n]
        return _fact
    
    _fact=_FACT_TABLE()
    
    
    def _sort(symbs):
        p_s=[x//2 for x in symbs.values() if x//2 > 0 ]
        m_s=[x%2 for x in symbs.values() if x%2 > 0 ]
        return p_s, m_s
       
       
    def _to_symbs(l, r):
        symbs={}
        for i in range(l-1,r):
            symbs[S[i]]=symbs.get(S[i],0)+1
        return symbs
        
        
    def _calculate(p_s, m_s):
        res=_fact(max(sum(p_s), 1))
        for x in p_s:
            res=int(res/_fact(x))
        res=res*max(len(m_s),1)
        return res
        
        
    def answerQuery(l, r):
        # Return the answer for this query modulo 1000000007.
        symbs=_to_symbs(l,r)
        if len(symbs)==1:
            return 1
        p_s, m_s=_sort(symbs)
        return _calculate(p_s, m_s)
       
    
  • + 0 comments
    #include <cstdio>
    #include <cmath>
    #include <iostream>
    #include <set>
    #include <algorithm>
    #include <vector>
    #include <map>
    #include <cassert>
    #include <string>
    #include <cstring>
    #include <queue>
    
    using namespace std;
    
    #define rep(i,a,b) for(int i = a; i < b; i++)
    #define S(x) scanf("%d",&x)
    #define S2(x,y) scanf("%d%d",&x,&y)
    #define P(x) printf("%d\n",x)
    #define all(v) v.begin(),v.end()
    #define FF first
    #define SS second
    #define pb push_back
    #define mp make_pair
    
    typedef long long int LL;
    typedef pair<int, int > pii;
    typedef vector<int > vi;
    
    const int mod = 1000000007;
    const int N = 100005;
    
    LL F[N], IF[N];
    
    LL _pow(LL a, LL b) {
      if(!b) return 1;
      if(b == 1) return a;
      if(b == 2) return a * a % mod;
      if(b & 1) {
        return a * _pow(a,b-1) % mod;
      }
      return _pow(_pow(a,b/2),2);
    }
    
    void pre() {
      F[0] = 1;
      rep(i,1,N) {
        F[i] = i * F[i-1] % mod;
      }
      IF[N - 1] = _pow(F[N - 1], mod - 2);
      for(int i = N - 2; i >= 0; i--) {
        IF[i] = IF[i + 1] * (i + 1) % mod;
      }
    }
    
    int X[26][N];
    
    int main() {
      pre();
      string s;
      cin >> s;
      int n = s.size();
      rep(i,0,n) {
        rep(j,0,26) X[j][i+1] = X[j][i];
        X[s[i]-'a'][i+1]++;
      }
      int q;
      S(q);
      while(q--) {
        int l,r;
        S2(l,r);
        int tot = 0;
        LL ans = 1;
        int odd = 0;
        rep(i,0,26) {
          int x = X[i][r] - X[i][l-1];
          odd += (x&1);
          ans *= IF[x / 2];
          ans %= mod;
          tot += x / 2;
        }
        ans *= F[tot];
        ans %= mod;
        if(odd) ans = ans * odd % mod;
        printf("%lld\n",ans);
      }
      return 0;
    }
    
  • + 0 comments

    The only trick here is to cache the frequency map, since you are going to use it for every single test in the test-range. Do not bother caching modinverse, etc. The real cost is the string manipulation. Do something like this in your initialize function

      std::vector<std::vector<int>> s_cumulativeFreq;
    
      void initialize(std::string s)
      {
        std::vector<std::vector<int>>(s.size()+1, std::vector<int>(26, 0)).swap(s_cumulativeFreq);
        for (size_t i = 0; i < s.size(); ++i) {
          auto& prevFreq = s_cumulativeFreq[i];
          auto& currFreq = s_cumulativeFreq[i + 1];
          std::copy(prevFreq.begin(), prevFreq.end(), currFreq.begin());
          ++currFreq[s[i] - 'a'];
      }
     }
    

    And of course, calculate the difference in frequencies...

    int answerQuery(int first, int last)
    {
      auto len = last - first + 1;
      if (len == 1)
        return 1;
    
      std::vector<int> freqs = s_cumulativeFreq[last];  // Compute the frequencies in the range [l, r)
      {
        auto leftIter = next(s_cumulativeFreq.begin(), first - 1); // I want to remove the entries BEFORE 'first'
        transform(freqs.begin(), freqs.end(), leftIter->begin(), freqs.begin(), std::minus<int>()); // Compute the frequencies in the range [l, r]
      }
    
      auto middleChars = std::count_if(freqs.begin(), freqs.end(), [](int f) {return f & 1; });
      // Now I divide by 2. I only care about the left side of the palindrome
      for_each(freqs.begin(), freqs.end(), [](auto& f) {f >>= 1; });
      freqs.erase(std::remove(freqs.begin(), freqs.end(), 0), freqs.end());
      // Total number of chars to permutate...
      auto palindromeChars = accumulate(freqs.begin(), freqs.end(), 0, std::plus<int>());
      ...
      You can fill the rest
      ...