约瑟夫问题
0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
这个问题是以弗拉维奥·约瑟夫命名的,他是1世纪的一名犹太历史学家。他在自己的日记中写道,他和他的40个战友被罗马军队包围在洞中。他们讨论是自杀还是被俘,最终决定自杀,并以抽签的方式决定谁杀掉谁。约瑟夫斯和另外一个人是最后两个留下的人。约瑟夫斯说服了那个人,他们将向罗马军队投降,不再自杀。约瑟夫斯把他的存活归因于运气或天意,他不知道是哪一个。 —— 【约瑟夫问题】维基百科
一、模拟单向环形链表
据题解大佬所言,LinkedList会超时,因为链表remove中删除复杂度为1,但查找到该索引需n;ArrayList相反是查找为1,删除为n。不过因为ArrayList删除是拷贝的后面元素是连续地址的,而链表需要大量访问非来纳许地址,因此耗时。
复杂度为O(n2)
class Solution { public int lastRemaining(int n, int m) { ArrayList<Integer> list = new ArrayList<>(n); for (int i = 0; i < n; i++) { list.add(i); } int idx = 0; while (n > 1) { idx = (idx + m - 1) % n; list.remove(idx); // 注意remove后idx自动后移 n--; } return list.get(0); } }
二、数学解法
f(n,m)与f(n-1,m)之间的关系:当n个数先删掉第一个坐标(m-1)%n时,可以看成n-1个数组成的环,其中首元素坐标为m。而f(n-1,m)的首元素坐标为0,因此 f(n,m) = ( f(n-1,m) + m ) % n。
class Solution { public int lastRemaining(int n, int m) { int ans = 0; // 最后一轮剩下2个人,所以从2开始反推 for (int i = 2; i <= n; i++) { ans = (ans + m) % i; } return ans; } }
三、马拉车算法 Manacher's algorithm
构造T,在每个字符两边添加‘#’,n+(n+1)统一成奇回文。 构造P对应以T中每个字符为中心的回文长度,寻找最大值。
1、最大半径减1等于最长回文串的长度
2、最长回文字符的起始位置(s中的索引)是中间位置(T或P中的索引)减去半径(P中的值+1)再除以2
这里的P为回文长度,因此无需与半径做转换。
public class Solution { 2 // Transform S into T. 3 // For example, S = "abba", T = "^#a#b#b#a#$". 4 // ^ and $ signs are sentinels appended to each end to avoid bounds checking 5 String preProcess(String s) { 6 int n = s.length(); 7 if (n == 0) return "^$"; 8 9 String ret = "^"; 10 for (int i = 0; i < n; i++) 11 { 12 ret += "#" + s.substring(i, i + 1); 13 } 14 15 ret += "#$"; 16 return ret; 17 } 18 public String longestPalindrome(String s) { 19 String T = preProcess(s); 20 int length = T.length(); 21 int[] p = new int[length]; 22 int C = 0, R = 0; 23 24 for (int i = 1; i < length - 1; i++) 25 { 26 int i_mirror = C - (i - C); 27 int diff = R - i; 28 if (diff >= 0)//当前i在C和R之间,可以利用回文的对称属性 29 { 30 if (p[i_mirror] < diff)//i的对称点的回文长度在C的大回文范围内部 31 { p[i] = p[i_mirror]; } 32 else 33 { 34 p[i] = diff; 35 //i处的回文可能超出C的大回文范围了 36 while (T.charAt(i + p[i] + 1) == T.charAt(i - p[i] - 1)) 37 { p[i]++; } 38 C = i; 39 R = i + p[i]; 40 } 41 } 42 else 43 { 44 p[i] = 0; 45 while (T.charAt(i + p[i] + 1) == T.charAt(i - p[i] - 1)) 46 { p[i]++; } 47 C = i; 48 R = i + p[i]; 49 } 50 } 51 52 int maxLen = 0; 53 int centerIndex = 0; 54 for (int i = 1; i < length - 1; i++) { 55 if (p[i] > maxLen) { 56 maxLen = p[i]; 57 centerIndex = i; 58 } 59 } 60 return s.substring((centerIndex - 1 - maxLen) / 2, (centerIndex - 1 - maxLen) / 2 + maxLen); 61 } 62 }