Rating changes for last rounds are temporarily rolled back. They will be returned soon. ×

raashidanwar's blog

By raashidanwar, 4 years ago, In English
  • Vote: I like it
  • +38
  • Vote: I do not like it

By raashidanwar, 4 years ago, In English

it's for beginners.

it's my first blog.

Here i'm discussing about a specific problem Minimal Rotation. how can we use hashing to solve this particular problem in O(nlog(n)) although we can solve it in O(n) using Booth's Algorithm

after understanding this blog you will be able to solve problems based on KMP, Booth's Algorithm, Manacher’s Algorithm without even knowing them but i insist you know those algorithms

Beauty of Hashing :

let's consider we have a string "abcd" first we need to construct prefix Hash array.

Hash[i + 1] = s[0] - 'a' + (s[1] - 'a') * 131 +++++++++ (s[i] - 'a') * (131 --- i times --- 131)

const int M1 = 1000000007;
int x = 1;
H[0] = 0;
for(int i = 0; i < n; i++) {
  Hash[i + 1] = (Hash[i] + x * (s[i] - 'a')) %M1;
  x *= 131;
  x %= M1;
}

you can see H[i+1] as a polynomial function with having coefficients (s[0]-'a', s[1]-'a', ....., s[i]-'a') and where we take x = 131

our aim is to get(l, r) = (s[l]-'a') + (s[l+1]-'a') * 131 +++++ (s[r]-'a') * 131... r — l times ...131 where l <= r

we have get(r) = Hash[r + 1] and get(l - 1) = Hash[l]

how can we get get(l, r)? yes you are right we are going use get(r) and get(l — 1)

get(r) - get(l - 1) = (s[l] - 'a') * (131 ... l times ... 131) + (s[l + 1] - 'a') * (131 ... (l + 1) times ... 131) ++++ (s[r] - 'a') * (131 ... r times ... 131)

if divide it by power(131, l) we get(l, r) = (s[l]-'a') + (s[l+1]-'a') * 131 +++++ (s[r]-'a') * 131... r - l times ...131

as we are using M1 = 1000000007

so for division purpose we will store inverse power of 131 p[i] = (1 / (131 ... i times ...)) % M1;

int x = 1;
for(int i = 0; i < n; i++) {
  p[i] = mpow(x, M1 - 2);
  x *= 131;
  x %= M1;
}

or

p[n - 1] = mpow(mpow(131, n - 1), M1 - 2);
for(int i = n - 2; ~i; i--)
  p[i] = (p[i + 1] * 131) % M1;

so finally we are here to find get(l, r)

Note get(l , r) { i am considering string from 1 to n instead of 0 to n — 1 }

int get(int l ,int r) {
  return ((Hash[r] - Hash[l - 1] + M1) * p[l - 1]) % M1;
}
  • if get(l1, r1) is equals to get(l2, r2) it's means both substrings are same ( 1 <= l1 <= r2 <= n and 1 <= l2 <= r2 <= n)

problem Minimal Rotation

first concatenate s = s + s because we know that s.substr(i, i + n - 1) is the i'th rotation of string s it's just for easy understanding. still you can do without concatenation but you must take care of rotation.

take k — 1 as the starting index of our minimal Lexicographically rotation string lets consider k = 1 ( string(0, n) is minimal string)

you must be thinking why not k = 0 as i told you i am taking string with 1 not with 0)

Now for ith rotation, the idea is to find the largest prefix that mathces with the prefix of current answer(starting at index k-1). This can be done using binary search over the largest length l[0,n-1] and check if get(i,i+l) equals get(k,k+l).

now iterate over string for 2 .... n (n is the length of string without concatenation)

and do binary search to find the maximum length l where s.substr(k - 1, l) is equals s.substr(i - 1, l)

if l equals n no further check both are same

else check for (l + 1)th character the one with minimum is the the answer for now

if s[k - 1 + l] > s[i - 1 + l]] k = i

else no change

s += s;
int k = 1;
int n = s.size() / 2;
for(int i = 2; i <= n; i++) {
  int lo = 0, hi = n - 1;
  while(lo <= hi) {
    int lm = (lo + hi) >> 1;
    if(get(i, i + lm) ==  get(k, k + lm))
      lo = lm + 1;
    else
      hi = lm - 1;
  }
  if(lo <= n - 1)
    if(s[i + lo - 1] < s[k + lo - 1])
      k = i;
}

Lexicographically minimal string is s.substr(k - 1, n)

full code

Full text and comments »

  • Vote: I like it
  • +30
  • Vote: I do not like it