[{"data":1,"prerenderedAt":1010},["ShallowReactive",2],{"guide-palindrome-detection-algorithms":3},{"id":4,"title":5,"body":6,"date":1002,"description":1003,"extension":1004,"meta":1005,"navigation":192,"path":1006,"readingTime":211,"seo":1007,"stem":1008,"__hash__":1009},"guides\u002Fguides\u002Fpalindrome-detection-algorithms.md","Palindrome Detection Algorithms: Techniques and Trade-offs",{"type":7,"value":8,"toc":991},"minimark",[9,13,18,21,120,127,130,134,137,272,275,278,282,285,508,511,515,518,597,600,619,720,723,727,730,833,836,840,917,920,924,950,954,975,979,987],[10,11,12],"p",{},"A palindrome reads the same forward and backward — \"racecar\", \"madam\", and \"A man, a plan, a canal: Panama\" are all palindromes. Detecting them is a classic programming exercise, but the problem reveals interesting trade-offs in algorithm design, character handling, and real-world text processing.",[14,15,17],"h2",{"id":16},"the-naive-approach-reverse-and-compare","The Naive Approach: Reverse and Compare",[10,19,20],{},"The simplest algorithm reverses the string and checks for equality:",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-javascript shiki shiki-themes github-light github-dark","function isPalindrome(str) {\n  const reversed = str.split('').reverse().join('')\n  return str === reversed\n}\n","javascript","",[29,30,31,55,99,114],"code",{"__ignoreMap":27},[32,33,36,40,44,48,52],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"szBVR","function",[32,41,43],{"class":42},"sScJk"," isPalindrome",[32,45,47],{"class":46},"sVt8B","(",[32,49,51],{"class":50},"s4XuR","str",[32,53,54],{"class":46},") {\n",[32,56,58,61,65,68,71,74,76,80,83,86,89,92,94,96],{"class":34,"line":57},2,[32,59,60],{"class":38},"  const",[32,62,64],{"class":63},"sj4cs"," reversed",[32,66,67],{"class":38}," =",[32,69,70],{"class":46}," str.",[32,72,73],{"class":42},"split",[32,75,47],{"class":46},[32,77,79],{"class":78},"sZZnC","''",[32,81,82],{"class":46},").",[32,84,85],{"class":42},"reverse",[32,87,88],{"class":46},"().",[32,90,91],{"class":42},"join",[32,93,47],{"class":46},[32,95,79],{"class":78},[32,97,98],{"class":46},")\n",[32,100,102,105,108,111],{"class":34,"line":101},3,[32,103,104],{"class":38},"  return",[32,106,107],{"class":46}," str ",[32,109,110],{"class":38},"===",[32,112,113],{"class":46}," reversed\n",[32,115,117],{"class":34,"line":116},4,[32,118,119],{"class":46},"}\n",[10,121,122,123,126],{},"This works for clean inputs like ",[29,124,125],{},"\"racecar\"",", but fails on real-world text that includes spaces, punctuation, and mixed case. It also creates a full copy of the string, using O(n) extra memory.",[10,128,129],{},"Time complexity: O(n). Space complexity: O(n).",[14,131,133],{"id":132},"the-two-pointer-technique","The Two-Pointer Technique",[10,135,136],{},"A more efficient approach uses two pointers — one starting at the beginning, one at the end — moving inward and comparing characters:",[22,138,140],{"className":24,"code":139,"language":26,"meta":27,"style":27},"function isPalindrome(str) {\n  let left = 0\n  let right = str.length - 1\n\n  while (left \u003C right) {\n    if (str[left] !== str[right]) return false\n    left++\n    right--\n  }\n\n  return true\n}\n",[29,141,142,154,168,188,194,209,230,239,248,254,259,267],{"__ignoreMap":27},[32,143,144,146,148,150,152],{"class":34,"line":35},[32,145,39],{"class":38},[32,147,43],{"class":42},[32,149,47],{"class":46},[32,151,51],{"class":50},[32,153,54],{"class":46},[32,155,156,159,162,165],{"class":34,"line":57},[32,157,158],{"class":38},"  let",[32,160,161],{"class":46}," left ",[32,163,164],{"class":38},"=",[32,166,167],{"class":63}," 0\n",[32,169,170,172,175,177,179,182,185],{"class":34,"line":101},[32,171,158],{"class":38},[32,173,174],{"class":46}," right ",[32,176,164],{"class":38},[32,178,70],{"class":46},[32,180,181],{"class":63},"length",[32,183,184],{"class":38}," -",[32,186,187],{"class":63}," 1\n",[32,189,190],{"class":34,"line":116},[32,191,193],{"emptyLinePlaceholder":192},true,"\n",[32,195,197,200,203,206],{"class":34,"line":196},5,[32,198,199],{"class":38},"  while",[32,201,202],{"class":46}," (left ",[32,204,205],{"class":38},"\u003C",[32,207,208],{"class":46}," right) {\n",[32,210,212,215,218,221,224,227],{"class":34,"line":211},6,[32,213,214],{"class":38},"    if",[32,216,217],{"class":46}," (str[left] ",[32,219,220],{"class":38},"!==",[32,222,223],{"class":46}," str[right]) ",[32,225,226],{"class":38},"return",[32,228,229],{"class":63}," false\n",[32,231,233,236],{"class":34,"line":232},7,[32,234,235],{"class":46},"    left",[32,237,238],{"class":38},"++\n",[32,240,242,245],{"class":34,"line":241},8,[32,243,244],{"class":46},"    right",[32,246,247],{"class":38},"--\n",[32,249,251],{"class":34,"line":250},9,[32,252,253],{"class":46},"  }\n",[32,255,257],{"class":34,"line":256},10,[32,258,193],{"emptyLinePlaceholder":192},[32,260,262,264],{"class":34,"line":261},11,[32,263,104],{"class":38},[32,265,266],{"class":63}," true\n",[32,268,270],{"class":34,"line":269},12,[32,271,119],{"class":46},[10,273,274],{},"This halves the number of comparisons and uses O(1) extra memory. No string copy is needed.",[10,276,277],{},"Time complexity: O(n). Space complexity: O(1).",[14,279,281],{"id":280},"ignoring-non-alphanumeric-characters","Ignoring Non-Alphanumeric Characters",[10,283,284],{},"Real-world palindromes like \"A man, a plan, a canal: Panama\" contain spaces and punctuation that should be ignored. You can handle this by preprocessing the string or by skipping non-alphanumeric characters in the two-pointer loop:",[22,286,288],{"className":24,"code":287,"language":26,"meta":27,"style":27},"function isPalindrome(str) {\n  let left = 0\n  let right = str.length - 1\n\n  while (left \u003C right) {\n    while (left \u003C right && !isAlphaNumeric(str[left])) left++\n    while (left \u003C right && !isAlphaNumeric(str[right])) right--\n\n    if (str[left].toLowerCase() !== str[right].toLowerCase()) return false\n\n    left++\n    right--\n  }\n\n  return true\n}\n\nfunction isAlphaNumeric(char) {\n  return \u002F[a-z0-9]\u002Fi.test(char)\n}\n",[29,289,290,302,312,328,332,342,367,388,392,419,423,429,435,440,445,452,457,462,477,503],{"__ignoreMap":27},[32,291,292,294,296,298,300],{"class":34,"line":35},[32,293,39],{"class":38},[32,295,43],{"class":42},[32,297,47],{"class":46},[32,299,51],{"class":50},[32,301,54],{"class":46},[32,303,304,306,308,310],{"class":34,"line":57},[32,305,158],{"class":38},[32,307,161],{"class":46},[32,309,164],{"class":38},[32,311,167],{"class":63},[32,313,314,316,318,320,322,324,326],{"class":34,"line":101},[32,315,158],{"class":38},[32,317,174],{"class":46},[32,319,164],{"class":38},[32,321,70],{"class":46},[32,323,181],{"class":63},[32,325,184],{"class":38},[32,327,187],{"class":63},[32,329,330],{"class":34,"line":116},[32,331,193],{"emptyLinePlaceholder":192},[32,333,334,336,338,340],{"class":34,"line":196},[32,335,199],{"class":38},[32,337,202],{"class":46},[32,339,205],{"class":38},[32,341,208],{"class":46},[32,343,344,347,349,351,353,356,359,362,365],{"class":34,"line":211},[32,345,346],{"class":38},"    while",[32,348,202],{"class":46},[32,350,205],{"class":38},[32,352,174],{"class":46},[32,354,355],{"class":38},"&&",[32,357,358],{"class":38}," !",[32,360,361],{"class":42},"isAlphaNumeric",[32,363,364],{"class":46},"(str[left])) left",[32,366,238],{"class":38},[32,368,369,371,373,375,377,379,381,383,386],{"class":34,"line":232},[32,370,346],{"class":38},[32,372,202],{"class":46},[32,374,205],{"class":38},[32,376,174],{"class":46},[32,378,355],{"class":38},[32,380,358],{"class":38},[32,382,361],{"class":42},[32,384,385],{"class":46},"(str[right])) right",[32,387,247],{"class":38},[32,389,390],{"class":34,"line":241},[32,391,193],{"emptyLinePlaceholder":192},[32,393,394,396,399,402,405,407,410,412,415,417],{"class":34,"line":250},[32,395,214],{"class":38},[32,397,398],{"class":46}," (str[left].",[32,400,401],{"class":42},"toLowerCase",[32,403,404],{"class":46},"() ",[32,406,220],{"class":38},[32,408,409],{"class":46}," str[right].",[32,411,401],{"class":42},[32,413,414],{"class":46},"()) ",[32,416,226],{"class":38},[32,418,229],{"class":63},[32,420,421],{"class":34,"line":256},[32,422,193],{"emptyLinePlaceholder":192},[32,424,425,427],{"class":34,"line":261},[32,426,235],{"class":46},[32,428,238],{"class":38},[32,430,431,433],{"class":34,"line":269},[32,432,244],{"class":46},[32,434,247],{"class":38},[32,436,438],{"class":34,"line":437},13,[32,439,253],{"class":46},[32,441,443],{"class":34,"line":442},14,[32,444,193],{"emptyLinePlaceholder":192},[32,446,448,450],{"class":34,"line":447},15,[32,449,104],{"class":38},[32,451,266],{"class":63},[32,453,455],{"class":34,"line":454},16,[32,456,119],{"class":46},[32,458,460],{"class":34,"line":459},17,[32,461,193],{"emptyLinePlaceholder":192},[32,463,465,467,470,472,475],{"class":34,"line":464},18,[32,466,39],{"class":38},[32,468,469],{"class":42}," isAlphaNumeric",[32,471,47],{"class":46},[32,473,474],{"class":50},"char",[32,476,54],{"class":46},[32,478,480,482,485,488,491,494,497,500],{"class":34,"line":479},19,[32,481,104],{"class":38},[32,483,484],{"class":78}," \u002F",[32,486,487],{"class":63},"[a-z0-9]",[32,489,490],{"class":78},"\u002F",[32,492,493],{"class":38},"i",[32,495,496],{"class":46},".",[32,498,499],{"class":42},"test",[32,501,502],{"class":46},"(char)\n",[32,504,506],{"class":34,"line":505},20,[32,507,119],{"class":46},[10,509,510],{},"This handles mixed case, spaces, and punctuation without creating a filtered copy of the string.",[14,512,514],{"id":513},"unicode-considerations","Unicode Considerations",[10,516,517],{},"JavaScript strings use UTF-16 encoding. Characters outside the Basic Multilingual Plane (emoji, some CJK characters) are represented as surrogate pairs. Naive reversal breaks these characters:",[22,519,521],{"className":24,"code":520,"language":26,"meta":27,"style":27},"\u002F\u002F Broken: splits surrogate pairs\n'top🥞pot'.split('').reverse().join('')  \u002F\u002F 'top��pot' (broken pancake emoji)\n\n\u002F\u002F Correct: use Array.from or spread syntax\n[...'top🥞pot'].reverse().join('')  \u002F\u002F 'top🥞pot' (intact)\n",[29,522,523,529,560,564,569],{"__ignoreMap":27},[32,524,525],{"class":34,"line":35},[32,526,528],{"class":527},"sJ8bj","\u002F\u002F Broken: splits surrogate pairs\n",[32,530,531,534,536,538,540,542,544,546,548,550,552,554,557],{"class":34,"line":57},[32,532,533],{"class":78},"'top🥞pot'",[32,535,496],{"class":46},[32,537,73],{"class":42},[32,539,47],{"class":46},[32,541,79],{"class":78},[32,543,82],{"class":46},[32,545,85],{"class":42},[32,547,88],{"class":46},[32,549,91],{"class":42},[32,551,47],{"class":46},[32,553,79],{"class":78},[32,555,556],{"class":46},")  ",[32,558,559],{"class":527},"\u002F\u002F 'top��pot' (broken pancake emoji)\n",[32,561,562],{"class":34,"line":101},[32,563,193],{"emptyLinePlaceholder":192},[32,565,566],{"class":34,"line":116},[32,567,568],{"class":527},"\u002F\u002F Correct: use Array.from or spread syntax\n",[32,570,571,574,577,579,582,584,586,588,590,592,594],{"class":34,"line":196},[32,572,573],{"class":46},"[",[32,575,576],{"class":38},"...",[32,578,533],{"class":78},[32,580,581],{"class":46},"].",[32,583,85],{"class":42},[32,585,88],{"class":46},[32,587,91],{"class":42},[32,589,47],{"class":46},[32,591,79],{"class":78},[32,593,556],{"class":46},[32,595,596],{"class":527},"\u002F\u002F 'top🥞pot' (intact)\n",[10,598,599],{},"For palindrome detection, the two-pointer approach naturally handles surrogate pairs because it compares characters from the outside in without reversing.",[10,601,602,603,606,607,610,611,614,615,618],{},"However, Unicode normalization adds another layer. The character ",[29,604,605],{},"é"," can be represented as a single code point (",[29,608,609],{},"U+00E9",") or as ",[29,612,613],{},"e"," + combining accent (",[29,616,617],{},"U+0065 U+0301","). These look identical but are not equal in string comparison:",[22,620,622],{"className":24,"code":621,"language":26,"meta":27,"style":27},"const a = '\\u00E9'       \u002F\u002F é (precomposed)\nconst b = 'e\\u0301'      \u002F\u002F é (decomposed)\na === b                  \u002F\u002F false!\n\n\u002F\u002F Normalize before comparing\na.normalize('NFC') === b.normalize('NFC')  \u002F\u002F true\n",[29,623,624,646,666,679,683,688],{"__ignoreMap":27},[32,625,626,629,632,634,637,640,643],{"class":34,"line":35},[32,627,628],{"class":38},"const",[32,630,631],{"class":63}," a",[32,633,67],{"class":38},[32,635,636],{"class":78}," '",[32,638,639],{"class":63},"\\u00E9",[32,641,642],{"class":78},"'",[32,644,645],{"class":527},"       \u002F\u002F é (precomposed)\n",[32,647,648,650,653,655,658,661,663],{"class":34,"line":57},[32,649,628],{"class":38},[32,651,652],{"class":63}," b",[32,654,67],{"class":38},[32,656,657],{"class":78}," 'e",[32,659,660],{"class":63},"\\u0301",[32,662,642],{"class":78},[32,664,665],{"class":527},"      \u002F\u002F é (decomposed)\n",[32,667,668,671,673,676],{"class":34,"line":101},[32,669,670],{"class":46},"a ",[32,672,110],{"class":38},[32,674,675],{"class":46}," b                  ",[32,677,678],{"class":527},"\u002F\u002F false!\n",[32,680,681],{"class":34,"line":116},[32,682,193],{"emptyLinePlaceholder":192},[32,684,685],{"class":34,"line":196},[32,686,687],{"class":527},"\u002F\u002F Normalize before comparing\n",[32,689,690,693,696,698,701,704,706,709,711,713,715,717],{"class":34,"line":211},[32,691,692],{"class":46},"a.",[32,694,695],{"class":42},"normalize",[32,697,47],{"class":46},[32,699,700],{"class":78},"'NFC'",[32,702,703],{"class":46},") ",[32,705,110],{"class":38},[32,707,708],{"class":46}," b.",[32,710,695],{"class":42},[32,712,47],{"class":46},[32,714,700],{"class":78},[32,716,556],{"class":46},[32,718,719],{"class":527},"\u002F\u002F true\n",[10,721,722],{},"For palindrome checking of user-submitted text, normalize the input first.",[14,724,726],{"id":725},"recursive-approach","Recursive Approach",[10,728,729],{},"Palindrome detection can be expressed recursively — a string is a palindrome if its first and last characters match and the substring between them is also a palindrome:",[22,731,733],{"className":24,"code":732,"language":26,"meta":27,"style":27},"function isPalindrome(str, left = 0, right = str.length - 1) {\n  if (left >= right) return true\n  if (str[left] !== str[right]) return false\n  return isPalindrome(str, left + 1, right - 1)\n}\n",[29,734,735,774,791,805,829],{"__ignoreMap":27},[32,736,737,739,741,743,745,748,751,753,756,758,761,763,765,767,769,772],{"class":34,"line":35},[32,738,39],{"class":38},[32,740,43],{"class":42},[32,742,47],{"class":46},[32,744,51],{"class":50},[32,746,747],{"class":46},", ",[32,749,750],{"class":50},"left",[32,752,67],{"class":38},[32,754,755],{"class":63}," 0",[32,757,747],{"class":46},[32,759,760],{"class":50},"right",[32,762,67],{"class":38},[32,764,70],{"class":46},[32,766,181],{"class":63},[32,768,184],{"class":38},[32,770,771],{"class":63}," 1",[32,773,54],{"class":46},[32,775,776,779,781,784,787,789],{"class":34,"line":57},[32,777,778],{"class":38},"  if",[32,780,202],{"class":46},[32,782,783],{"class":38},">=",[32,785,786],{"class":46}," right) ",[32,788,226],{"class":38},[32,790,266],{"class":63},[32,792,793,795,797,799,801,803],{"class":34,"line":101},[32,794,778],{"class":38},[32,796,217],{"class":46},[32,798,220],{"class":38},[32,800,223],{"class":46},[32,802,226],{"class":38},[32,804,229],{"class":63},[32,806,807,809,811,814,817,819,822,825,827],{"class":34,"line":116},[32,808,104],{"class":38},[32,810,43],{"class":42},[32,812,813],{"class":46},"(str, left ",[32,815,816],{"class":38},"+",[32,818,771],{"class":63},[32,820,821],{"class":46},", right ",[32,823,824],{"class":38},"-",[32,826,771],{"class":63},[32,828,98],{"class":46},[32,830,831],{"class":34,"line":196},[32,832,119],{"class":46},[10,834,835],{},"This is elegant but risks stack overflow on very long strings (JavaScript's call stack limit is typically around 10,000–25,000 frames). The iterative two-pointer approach is safer for production use.",[14,837,839],{"id":838},"comparative-summary","Comparative Summary",[841,842,843,862],"table",{},[844,845,846],"thead",{},[847,848,849,853,856,859],"tr",{},[850,851,852],"th",{},"Algorithm",[850,854,855],{},"Time",[850,857,858],{},"Space",[850,860,861],{},"Notes",[863,864,865,879,892,904],"tbody",{},[847,866,867,871,874,876],{},[868,869,870],"td",{},"Reverse and compare",[868,872,873],{},"O(n)",[868,875,873],{},[868,877,878],{},"Simple, allocates copy",[847,880,881,884,886,889],{},[868,882,883],{},"Two-pointer",[868,885,873],{},[868,887,888],{},"O(1)",[868,890,891],{},"Best for most use cases",[847,893,894,897,899,901],{},[868,895,896],{},"Two-pointer with skip",[868,898,873],{},[868,900,888],{},[868,902,903],{},"Handles punctuation\u002Fcase",[847,905,906,909,911,914],{},[868,907,908],{},"Recursive",[868,910,873],{},[868,912,913],{},"O(n) stack",[868,915,916],{},"Elegant, stack overflow risk",[10,918,919],{},"For most practical applications, the two-pointer approach with character skipping strikes the best balance of clarity, performance, and correctness.",[14,921,923],{"id":922},"key-takeaways","Key Takeaways",[925,926,927,931,934,941,944,947],"ul",{},[928,929,930],"li",{},"The two-pointer technique detects palindromes in O(n) time with O(1) space — no string copy needed",[928,932,933],{},"Real-world palindrome checking must handle case, spaces, and punctuation by skipping non-alphanumeric characters",[928,935,936,937,940],{},"UTF-16 surrogate pairs break naive string reversal — use ",[29,938,939],{},"Array.from()"," or spread syntax",[928,942,943],{},"Unicode normalization (NFC) ensures accented characters compare correctly",[928,945,946],{},"Recursive approaches are elegant but risk stack overflow on long strings",[928,948,949],{},"The two-pointer method with character skipping is the best general-purpose solution",[14,951,953],{"id":952},"related-guides","Related Guides",[925,955,956,963,969],{},[928,957,958],{},[959,960,962],"a",{"href":961},"\u002Fguides\u002Ftext-reversal-techniques","Text Reversal Techniques: Methods and Applications",[928,964,965],{},[959,966,968],{"href":967},"\u002Fguides\u002Fmirror-text-guide","Mirror Text Guide: How Reversed Text Works",[928,970,971],{},[959,972,974],{"href":973},"\u002Fguides\u002Ftext-manipulation-tools","Text Manipulation Tools: A Practical Overview",[14,976,978],{"id":977},"try-it-yourself","Try It Yourself",[10,980,981,982,986],{},"Test your strings instantly with our free ",[959,983,985],{"href":984},"\u002Ftools\u002Ftext-reverser","Text Reverser"," tool. Enter any text, see it reversed — and discover palindromes by comparing the original and reversed output side by side.",[988,989,990],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":27,"searchDepth":57,"depth":57,"links":992},[993,994,995,996,997,998,999,1000,1001],{"id":16,"depth":57,"text":17},{"id":132,"depth":57,"text":133},{"id":280,"depth":57,"text":281},{"id":513,"depth":57,"text":514},{"id":725,"depth":57,"text":726},{"id":838,"depth":57,"text":839},{"id":922,"depth":57,"text":923},{"id":952,"depth":57,"text":953},{"id":977,"depth":57,"text":978},"2026-05-28","Explore palindrome detection algorithms — from simple string reversal to two-pointer techniques and Unicode-aware approaches with practical code examples.","md",{"immutable":192},"\u002Fguides\u002Fpalindrome-detection-algorithms",{"title":5,"description":1003},"guides\u002Fpalindrome-detection-algorithms","lkd8rYAtpJGMjpbx6ufNTUqUR2fSQfqUZEIT_z7-TWY",1780401335572]